61d8c8ab8f2507bdf8013bceefea0520c11ae148
[reactos.git] / drivers / filesystems / ntfs / attrib.c
1 /*
2 * ReactOS kernel
3 * Copyright (C) 2002,2003 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/attrib.c
22 * PURPOSE: NTFS filesystem driver
23 * PROGRAMMERS: Eric Kohl
24 * Valentin Verkhovsky
25 * Hervé Poussineau (hpoussin@reactos.org)
26 * Pierre Schweitzer (pierre@reactos.org)
27 */
28
29 /* INCLUDES *****************************************************************/
30
31 #include "ntfs.h"
32
33 #define NDEBUG
34 #include <debug.h>
35
36 /* FUNCTIONS ****************************************************************/
37
38 /**
39 * @name AddRun
40 * @implemented
41 *
42 * Adds a run of allocated clusters to a non-resident attribute.
43 *
44 * @param Vcb
45 * Pointer to an NTFS_VCB for the destination volume.
46 *
47 * @param AttrContext
48 * Pointer to an NTFS_ATTR_CONTEXT describing the destination attribute.
49 *
50 * @param AttrOffset
51 * Byte offset of the destination attribute relative to its file record.
52 *
53 * @param FileRecord
54 * Pointer to a complete copy of the file record containing the destination attribute. Must be at least
55 * Vcb->NtfsInfo.BytesPerFileRecord bytes long.
56 *
57 * @param NextAssignedCluster
58 * Logical cluster number of the start of the data run being added.
59 *
60 * @param RunLength
61 * How many clusters are in the data run being added. Can't be 0.
62 *
63 * @return
64 * STATUS_SUCCESS on success. STATUS_INVALID_PARAMETER if AttrContext describes a resident attribute.
65 * STATUS_INSUFFICIENT_RESOURCES if ConvertDataRunsToLargeMCB() fails.
66 * STATUS_BUFFER_TOO_SMALL if ConvertLargeMCBToDataRuns() fails.
67 * STATUS_NOT_IMPLEMENTED if we need to migrate the attribute to an attribute list (TODO).
68 *
69 * @remarks
70 * Clusters should have been allocated previously with NtfsAllocateClusters().
71 *
72 *
73 */
74 NTSTATUS
75 AddRun(PNTFS_VCB Vcb,
76 PNTFS_ATTR_CONTEXT AttrContext,
77 ULONG AttrOffset,
78 PFILE_RECORD_HEADER FileRecord,
79 ULONGLONG NextAssignedCluster,
80 ULONG RunLength)
81 {
82 NTSTATUS Status;
83 PUCHAR DataRun = (PUCHAR)&AttrContext->Record + AttrContext->Record.NonResident.MappingPairsOffset;
84 int DataRunMaxLength;
85 PNTFS_ATTR_RECORD DestinationAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + AttrOffset);
86 LARGE_MCB DataRunsMCB;
87 ULONG NextAttributeOffset = AttrOffset + AttrContext->Record.Length;
88 ULONGLONG NextVBN = AttrContext->Record.NonResident.LowestVCN;
89
90 // Allocate some memory for the RunBuffer
91 PUCHAR RunBuffer;
92 int RunBufferOffset = 0;
93
94 if (!AttrContext->Record.IsNonResident)
95 return STATUS_INVALID_PARAMETER;
96
97 RunBuffer = ExAllocatePoolWithTag(NonPagedPool, Vcb->NtfsInfo.BytesPerFileRecord, TAG_NTFS);
98
99 // Convert the data runs to a map control block
100 Status = ConvertDataRunsToLargeMCB(DataRun, &DataRunsMCB, &NextVBN);
101 if (!NT_SUCCESS(Status))
102 {
103 DPRINT1("Unable to convert data runs to MCB (probably ran out of memory)!\n");
104 ExFreePoolWithTag(RunBuffer, TAG_NTFS);
105 return Status;
106 }
107
108 // Add newly-assigned clusters to mcb
109 _SEH2_TRY{
110 if (!FsRtlAddLargeMcbEntry(&DataRunsMCB,
111 NextVBN,
112 NextAssignedCluster,
113 RunLength))
114 {
115 FsRtlUninitializeLargeMcb(&DataRunsMCB);
116 ExFreePoolWithTag(RunBuffer, TAG_NTFS);
117 return STATUS_INSUFFICIENT_RESOURCES;
118 }
119 } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
120 FsRtlUninitializeLargeMcb(&DataRunsMCB);
121 ExFreePoolWithTag(RunBuffer, TAG_NTFS);
122 _SEH2_YIELD(return STATUS_INSUFFICIENT_RESOURCES);
123 } _SEH2_END;
124
125
126 // Convert the map control block back to encoded data runs
127 ConvertLargeMCBToDataRuns(&DataRunsMCB, RunBuffer, Vcb->NtfsInfo.BytesPerCluster, &RunBufferOffset);
128
129 // Get the amount of free space between the start of the of the first data run and the attribute end
130 DataRunMaxLength = AttrContext->Record.Length - AttrContext->Record.NonResident.MappingPairsOffset;
131
132 // Do we need to extend the attribute (or convert to attribute list)?
133 if (DataRunMaxLength < RunBufferOffset)
134 {
135 PNTFS_ATTR_RECORD NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + NextAttributeOffset);
136 DataRunMaxLength += Vcb->NtfsInfo.BytesPerFileRecord - NextAttributeOffset - (sizeof(ULONG) * 2);
137
138 // Can we move the end of the attribute?
139 if (NextAttribute->Type != AttributeEnd || DataRunMaxLength < RunBufferOffset - 1)
140 {
141 DPRINT1("FIXME: Need to create attribute list! Max Data Run Length available: %d\n", DataRunMaxLength);
142 if (NextAttribute->Type != AttributeEnd)
143 DPRINT1("There's another attribute after this one with type %0xlx\n", NextAttribute->Type);
144 ExFreePoolWithTag(RunBuffer, TAG_NTFS);
145 FsRtlUninitializeLargeMcb(&DataRunsMCB);
146 return STATUS_NOT_IMPLEMENTED;
147 }
148
149 // calculate position of end markers
150 NextAttributeOffset = AttrOffset + AttrContext->Record.NonResident.MappingPairsOffset + RunBufferOffset;
151 NextAttributeOffset = ALIGN_UP_BY(NextAttributeOffset, 8);
152
153 // Write the end markers
154 NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + NextAttributeOffset);
155 NextAttribute->Type = AttributeEnd;
156 NextAttribute->Length = FILE_RECORD_END;
157
158 // Update the length
159 DestinationAttribute->Length = NextAttributeOffset - AttrOffset;
160 AttrContext->Record.Length = DestinationAttribute->Length;
161
162 // We need to increase the FileRecord size
163 FileRecord->BytesInUse = NextAttributeOffset + (sizeof(ULONG) * 2);
164 }
165
166 // NOTE: from this point on the original attribute record will contain invalid data in it's runbuffer
167 // TODO: Elegant fix? Could we free the old Record and allocate a new one without issue?
168
169 // Update HighestVCN
170 DestinationAttribute->NonResident.HighestVCN =
171 AttrContext->Record.NonResident.HighestVCN = max(NextVBN - 1 + RunLength,
172 AttrContext->Record.NonResident.HighestVCN);
173
174 // Write data runs to destination attribute
175 RtlCopyMemory((PVOID)((ULONG_PTR)DestinationAttribute + DestinationAttribute->NonResident.MappingPairsOffset),
176 RunBuffer,
177 RunBufferOffset);
178
179 // Update the file record
180 Status = UpdateFileRecord(Vcb, AttrContext->FileMFTIndex, FileRecord);
181
182 ExFreePoolWithTag(RunBuffer, TAG_NTFS);
183 FsRtlUninitializeLargeMcb(&DataRunsMCB);
184
185 NtfsDumpDataRuns((PUCHAR)((ULONG_PTR)DestinationAttribute + DestinationAttribute->NonResident.MappingPairsOffset), 0);
186
187 return Status;
188 }
189
190 /**
191 * @name ConvertDataRunsToLargeMCB
192 * @implemented
193 *
194 * Converts binary data runs to a map control block.
195 *
196 * @param DataRun
197 * Pointer to the run data
198 *
199 * @param DataRunsMCB
200 * Pointer to an unitialized LARGE_MCB structure.
201 *
202 * @return
203 * STATUS_SUCCESS on success, STATUS_INSUFFICIENT_RESOURCES if we fail to
204 * initialize the mcb or add an entry.
205 *
206 * @remarks
207 * Initializes the LARGE_MCB pointed to by DataRunsMCB. If this function succeeds, you
208 * need to call FsRtlUninitializeLargeMcb() when you're done with DataRunsMCB. This
209 * function will ensure the LargeMCB has been unitialized in case of failure.
210 *
211 */
212 NTSTATUS
213 ConvertDataRunsToLargeMCB(PUCHAR DataRun,
214 PLARGE_MCB DataRunsMCB,
215 PULONGLONG pNextVBN)
216 {
217 LONGLONG DataRunOffset;
218 ULONGLONG DataRunLength;
219 LONGLONG DataRunStartLCN;
220 ULONGLONG NextCluster;
221
222 ULONGLONG LastLCN = 0;
223
224 // Initialize the MCB, potentially catch an exception
225 _SEH2_TRY{
226 FsRtlInitializeLargeMcb(DataRunsMCB, NonPagedPool);
227 } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
228 _SEH2_YIELD(return STATUS_INSUFFICIENT_RESOURCES);
229 } _SEH2_END;
230
231 while (*DataRun != 0)
232 {
233 DataRun = DecodeRun(DataRun, &DataRunOffset, &DataRunLength);
234
235 if (DataRunOffset != -1)
236 {
237 // Normal data run.
238 DataRunStartLCN = LastLCN + DataRunOffset;
239 LastLCN = DataRunStartLCN;
240 NextCluster = LastLCN + DataRunLength;
241
242
243 _SEH2_TRY{
244 if (!FsRtlAddLargeMcbEntry(DataRunsMCB,
245 *pNextVBN,
246 DataRunStartLCN,
247 DataRunLength))
248 {
249 FsRtlUninitializeLargeMcb(DataRunsMCB);
250 return STATUS_INSUFFICIENT_RESOURCES;
251 }
252 } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
253 FsRtlUninitializeLargeMcb(DataRunsMCB);
254 _SEH2_YIELD(return STATUS_INSUFFICIENT_RESOURCES);
255 } _SEH2_END;
256
257 }
258
259 *pNextVBN += DataRunLength;
260 }
261
262 return STATUS_SUCCESS;
263 }
264
265 /**
266 * @name ConvertLargeMCBToDataRuns
267 * @implemented
268 *
269 * Converts a map control block to a series of encoded data runs (used by non-resident attributes).
270 *
271 * @param DataRunsMCB
272 * Pointer to a LARGE_MCB structure describing the data runs.
273 *
274 * @param RunBuffer
275 * Pointer to the buffer that will receive the encoded data runs.
276 *
277 * @param MaxBufferSize
278 * Size of RunBuffer, in bytes.
279 *
280 * @param UsedBufferSize
281 * Pointer to a ULONG that will receive the size of the data runs in bytes. Can't be NULL.
282 *
283 * @return
284 * STATUS_SUCCESS on success, STATUS_BUFFER_TOO_SMALL if RunBuffer is too small to contain the
285 * complete output.
286 *
287 */
288 NTSTATUS
289 ConvertLargeMCBToDataRuns(PLARGE_MCB DataRunsMCB,
290 PUCHAR RunBuffer,
291 ULONG MaxBufferSize,
292 PULONG UsedBufferSize)
293 {
294 NTSTATUS Status = STATUS_SUCCESS;
295 ULONG RunBufferOffset = 0;
296 LONGLONG DataRunOffset;
297 ULONGLONG LastLCN = 0;
298
299 LONGLONG Vbn, Lbn, Count;
300
301
302 DPRINT("\t[Vbn, Lbn, Count]\n");
303
304 // convert each mcb entry to a data run
305 for (int i = 0; FsRtlGetNextLargeMcbEntry(DataRunsMCB, i, &Vbn, &Lbn, &Count); i++)
306 {
307 UCHAR DataRunOffsetSize = 0;
308 UCHAR DataRunLengthSize = 0;
309 UCHAR ControlByte = 0;
310
311 // [vbn, lbn, count]
312 DPRINT("\t[%I64d, %I64d,%I64d]\n", Vbn, Lbn, Count);
313
314 // TODO: check for holes and convert to sparse runs
315 DataRunOffset = Lbn - LastLCN;
316 LastLCN = Lbn;
317
318 // now we need to determine how to represent DataRunOffset with the minimum number of bytes
319 DPRINT("Determining how many bytes needed to represent %I64x\n", DataRunOffset);
320 DataRunOffsetSize = GetPackedByteCount(DataRunOffset, TRUE);
321 DPRINT("%d bytes needed.\n", DataRunOffsetSize);
322
323 // determine how to represent DataRunLengthSize with the minimum number of bytes
324 DPRINT("Determining how many bytes needed to represent %I64x\n", Count);
325 DataRunLengthSize = GetPackedByteCount(Count, TRUE);
326 DPRINT("%d bytes needed.\n", DataRunLengthSize);
327
328 // ensure the next data run + end marker would be > Max buffer size
329 if (RunBufferOffset + 2 + DataRunLengthSize + DataRunOffsetSize > MaxBufferSize)
330 {
331 Status = STATUS_BUFFER_TOO_SMALL;
332 DPRINT1("FIXME: Ran out of room in buffer for data runs!\n");
333 break;
334 }
335
336 // pack and copy the control byte
337 ControlByte = (DataRunOffsetSize << 4) + DataRunLengthSize;
338 RunBuffer[RunBufferOffset++] = ControlByte;
339
340 // copy DataRunLength
341 RtlCopyMemory(RunBuffer + RunBufferOffset, &Count, DataRunLengthSize);
342 RunBufferOffset += DataRunLengthSize;
343
344 // copy DataRunOffset
345 RtlCopyMemory(RunBuffer + RunBufferOffset, &DataRunOffset, DataRunOffsetSize);
346 RunBufferOffset += DataRunOffsetSize;
347 }
348
349 // End of data runs
350 RunBuffer[RunBufferOffset++] = 0;
351
352 *UsedBufferSize = RunBufferOffset;
353 DPRINT("New Size of DataRuns: %ld\n", *UsedBufferSize);
354
355 return Status;
356 }
357
358 PUCHAR
359 DecodeRun(PUCHAR DataRun,
360 LONGLONG *DataRunOffset,
361 ULONGLONG *DataRunLength)
362 {
363 UCHAR DataRunOffsetSize;
364 UCHAR DataRunLengthSize;
365 CHAR i;
366
367 DataRunOffsetSize = (*DataRun >> 4) & 0xF;
368 DataRunLengthSize = *DataRun & 0xF;
369 *DataRunOffset = 0;
370 *DataRunLength = 0;
371 DataRun++;
372 for (i = 0; i < DataRunLengthSize; i++)
373 {
374 *DataRunLength += ((ULONG64)*DataRun) << (i * 8);
375 DataRun++;
376 }
377
378 /* NTFS 3+ sparse files */
379 if (DataRunOffsetSize == 0)
380 {
381 *DataRunOffset = -1;
382 }
383 else
384 {
385 for (i = 0; i < DataRunOffsetSize - 1; i++)
386 {
387 *DataRunOffset += ((ULONG64)*DataRun) << (i * 8);
388 DataRun++;
389 }
390 /* The last byte contains sign so we must process it different way. */
391 *DataRunOffset = ((LONG64)(CHAR)(*(DataRun++)) << (i * 8)) + *DataRunOffset;
392 }
393
394 DPRINT("DataRunOffsetSize: %x\n", DataRunOffsetSize);
395 DPRINT("DataRunLengthSize: %x\n", DataRunLengthSize);
396 DPRINT("DataRunOffset: %x\n", *DataRunOffset);
397 DPRINT("DataRunLength: %x\n", *DataRunLength);
398
399 return DataRun;
400 }
401
402 BOOLEAN
403 FindRun(PNTFS_ATTR_RECORD NresAttr,
404 ULONGLONG vcn,
405 PULONGLONG lcn,
406 PULONGLONG count)
407 {
408 if (vcn < NresAttr->NonResident.LowestVCN || vcn > NresAttr->NonResident.HighestVCN)
409 return FALSE;
410
411 DecodeRun((PUCHAR)((ULONG_PTR)NresAttr + NresAttr->NonResident.MappingPairsOffset), (PLONGLONG)lcn, count);
412
413 return TRUE;
414 }
415
416 static
417 NTSTATUS
418 InternalReadNonResidentAttributes(PFIND_ATTR_CONTXT Context)
419 {
420 ULONGLONG ListSize;
421 PNTFS_ATTR_RECORD Attribute;
422 PNTFS_ATTR_CONTEXT ListContext;
423
424 DPRINT("InternalReadNonResidentAttributes(%p)\n", Context);
425
426 Attribute = Context->CurrAttr;
427 ASSERT(Attribute->Type == AttributeAttributeList);
428
429 if (Context->OnlyResident)
430 {
431 Context->NonResidentStart = NULL;
432 Context->NonResidentEnd = NULL;
433 return STATUS_SUCCESS;
434 }
435
436 if (Context->NonResidentStart != NULL)
437 {
438 return STATUS_FILE_CORRUPT_ERROR;
439 }
440
441 ListContext = PrepareAttributeContext(Attribute);
442 ListSize = AttributeDataLength(&ListContext->Record);
443 if (ListSize > 0xFFFFFFFF)
444 {
445 ReleaseAttributeContext(ListContext);
446 return STATUS_BUFFER_OVERFLOW;
447 }
448
449 Context->NonResidentStart = ExAllocatePoolWithTag(NonPagedPool, (ULONG)ListSize, TAG_NTFS);
450 if (Context->NonResidentStart == NULL)
451 {
452 ReleaseAttributeContext(ListContext);
453 return STATUS_INSUFFICIENT_RESOURCES;
454 }
455
456 if (ReadAttribute(Context->Vcb, ListContext, 0, (PCHAR)Context->NonResidentStart, (ULONG)ListSize) != ListSize)
457 {
458 ExFreePoolWithTag(Context->NonResidentStart, TAG_NTFS);
459 Context->NonResidentStart = NULL;
460 ReleaseAttributeContext(ListContext);
461 return STATUS_FILE_CORRUPT_ERROR;
462 }
463
464 ReleaseAttributeContext(ListContext);
465 Context->NonResidentEnd = (PNTFS_ATTR_RECORD)((PCHAR)Context->NonResidentStart + ListSize);
466 return STATUS_SUCCESS;
467 }
468
469 static
470 PNTFS_ATTR_RECORD
471 InternalGetNextAttribute(PFIND_ATTR_CONTXT Context)
472 {
473 PNTFS_ATTR_RECORD NextAttribute;
474
475 if (Context->CurrAttr == (PVOID)-1)
476 {
477 return NULL;
478 }
479
480 if (Context->CurrAttr >= Context->FirstAttr &&
481 Context->CurrAttr < Context->LastAttr)
482 {
483 if (Context->CurrAttr->Length == 0)
484 {
485 DPRINT1("Broken length!\n");
486 Context->CurrAttr = (PVOID)-1;
487 return NULL;
488 }
489
490 NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)Context->CurrAttr + Context->CurrAttr->Length);
491
492 if (NextAttribute > Context->LastAttr || NextAttribute < Context->FirstAttr)
493 {
494 DPRINT1("Broken length: 0x%lx!\n", Context->CurrAttr->Length);
495 Context->CurrAttr = (PVOID)-1;
496 return NULL;
497 }
498
499 Context->Offset += ((ULONG_PTR)NextAttribute - (ULONG_PTR)Context->CurrAttr);
500 Context->CurrAttr = NextAttribute;
501
502 if (Context->CurrAttr < Context->LastAttr &&
503 Context->CurrAttr->Type != AttributeEnd)
504 {
505 return Context->CurrAttr;
506 }
507 }
508
509 if (Context->NonResidentStart == NULL)
510 {
511 Context->CurrAttr = (PVOID)-1;
512 return NULL;
513 }
514
515 if (Context->CurrAttr < Context->NonResidentStart ||
516 Context->CurrAttr >= Context->NonResidentEnd)
517 {
518 Context->CurrAttr = Context->NonResidentStart;
519 }
520 else if (Context->CurrAttr->Length != 0)
521 {
522 NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)Context->CurrAttr + Context->CurrAttr->Length);
523 Context->Offset += ((ULONG_PTR)NextAttribute - (ULONG_PTR)Context->CurrAttr);
524 Context->CurrAttr = NextAttribute;
525 }
526 else
527 {
528 DPRINT1("Broken length!\n");
529 Context->CurrAttr = (PVOID)-1;
530 return NULL;
531 }
532
533 if (Context->CurrAttr < Context->NonResidentEnd &&
534 Context->CurrAttr->Type != AttributeEnd)
535 {
536 return Context->CurrAttr;
537 }
538
539 Context->CurrAttr = (PVOID)-1;
540 return NULL;
541 }
542
543 NTSTATUS
544 FindFirstAttribute(PFIND_ATTR_CONTXT Context,
545 PDEVICE_EXTENSION Vcb,
546 PFILE_RECORD_HEADER FileRecord,
547 BOOLEAN OnlyResident,
548 PNTFS_ATTR_RECORD * Attribute)
549 {
550 NTSTATUS Status;
551
552 DPRINT("FindFistAttribute(%p, %p, %p, %p, %u, %p)\n", Context, Vcb, FileRecord, OnlyResident, Attribute);
553
554 Context->Vcb = Vcb;
555 Context->OnlyResident = OnlyResident;
556 Context->FirstAttr = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + FileRecord->AttributeOffset);
557 Context->CurrAttr = Context->FirstAttr;
558 Context->LastAttr = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + FileRecord->BytesInUse);
559 Context->NonResidentStart = NULL;
560 Context->NonResidentEnd = NULL;
561 Context->Offset = FileRecord->AttributeOffset;
562
563 if (Context->FirstAttr->Type == AttributeEnd)
564 {
565 Context->CurrAttr = (PVOID)-1;
566 return STATUS_END_OF_FILE;
567 }
568 else if (Context->FirstAttr->Type == AttributeAttributeList)
569 {
570 Status = InternalReadNonResidentAttributes(Context);
571 if (!NT_SUCCESS(Status))
572 {
573 return Status;
574 }
575
576 *Attribute = InternalGetNextAttribute(Context);
577 if (*Attribute == NULL)
578 {
579 return STATUS_END_OF_FILE;
580 }
581 }
582 else
583 {
584 *Attribute = Context->CurrAttr;
585 Context->Offset = (UCHAR*)Context->CurrAttr - (UCHAR*)FileRecord;
586 }
587
588 return STATUS_SUCCESS;
589 }
590
591 NTSTATUS
592 FindNextAttribute(PFIND_ATTR_CONTXT Context,
593 PNTFS_ATTR_RECORD * Attribute)
594 {
595 NTSTATUS Status;
596
597 DPRINT("FindNextAttribute(%p, %p)\n", Context, Attribute);
598
599 *Attribute = InternalGetNextAttribute(Context);
600 if (*Attribute == NULL)
601 {
602 return STATUS_END_OF_FILE;
603 }
604
605 if (Context->CurrAttr->Type != AttributeAttributeList)
606 {
607 return STATUS_SUCCESS;
608 }
609
610 Status = InternalReadNonResidentAttributes(Context);
611 if (!NT_SUCCESS(Status))
612 {
613 return Status;
614 }
615
616 *Attribute = InternalGetNextAttribute(Context);
617 if (*Attribute == NULL)
618 {
619 return STATUS_END_OF_FILE;
620 }
621
622 return STATUS_SUCCESS;
623 }
624
625 VOID
626 FindCloseAttribute(PFIND_ATTR_CONTXT Context)
627 {
628 if (Context->NonResidentStart != NULL)
629 {
630 ExFreePoolWithTag(Context->NonResidentStart, TAG_NTFS);
631 Context->NonResidentStart = NULL;
632 }
633 }
634
635 static
636 VOID
637 NtfsDumpFileNameAttribute(PNTFS_ATTR_RECORD Attribute)
638 {
639 PFILENAME_ATTRIBUTE FileNameAttr;
640
641 DbgPrint(" $FILE_NAME ");
642
643 // DbgPrint(" Length %lu Offset %hu ", Attribute->Resident.ValueLength, Attribute->Resident.ValueOffset);
644
645 FileNameAttr = (PFILENAME_ATTRIBUTE)((ULONG_PTR)Attribute + Attribute->Resident.ValueOffset);
646 DbgPrint(" (%x) '%.*S' ", FileNameAttr->NameType, FileNameAttr->NameLength, FileNameAttr->Name);
647 DbgPrint(" '%x' \n", FileNameAttr->FileAttributes);
648 DbgPrint(" AllocatedSize: %I64u\nDataSize: %I64u\n", FileNameAttr->AllocatedSize, FileNameAttr->DataSize);
649 }
650
651
652 static
653 VOID
654 NtfsDumpStandardInformationAttribute(PNTFS_ATTR_RECORD Attribute)
655 {
656 PSTANDARD_INFORMATION StandardInfoAttr;
657
658 DbgPrint(" $STANDARD_INFORMATION ");
659
660 // DbgPrint(" Length %lu Offset %hu ", Attribute->Resident.ValueLength, Attribute->Resident.ValueOffset);
661
662 StandardInfoAttr = (PSTANDARD_INFORMATION)((ULONG_PTR)Attribute + Attribute->Resident.ValueOffset);
663 DbgPrint(" '%x' ", StandardInfoAttr->FileAttribute);
664 }
665
666
667 static
668 VOID
669 NtfsDumpVolumeNameAttribute(PNTFS_ATTR_RECORD Attribute)
670 {
671 PWCHAR VolumeName;
672
673 DbgPrint(" $VOLUME_NAME ");
674
675 // DbgPrint(" Length %lu Offset %hu ", Attribute->Resident.ValueLength, Attribute->Resident.ValueOffset);
676
677 VolumeName = (PWCHAR)((ULONG_PTR)Attribute + Attribute->Resident.ValueOffset);
678 DbgPrint(" '%.*S' ", Attribute->Resident.ValueLength / sizeof(WCHAR), VolumeName);
679 }
680
681
682 static
683 VOID
684 NtfsDumpVolumeInformationAttribute(PNTFS_ATTR_RECORD Attribute)
685 {
686 PVOLINFO_ATTRIBUTE VolInfoAttr;
687
688 DbgPrint(" $VOLUME_INFORMATION ");
689
690 // DbgPrint(" Length %lu Offset %hu ", Attribute->Resident.ValueLength, Attribute->Resident.ValueOffset);
691
692 VolInfoAttr = (PVOLINFO_ATTRIBUTE)((ULONG_PTR)Attribute + Attribute->Resident.ValueOffset);
693 DbgPrint(" NTFS Version %u.%u Flags 0x%04hx ",
694 VolInfoAttr->MajorVersion,
695 VolInfoAttr->MinorVersion,
696 VolInfoAttr->Flags);
697 }
698
699
700 static
701 VOID
702 NtfsDumpIndexRootAttribute(PNTFS_ATTR_RECORD Attribute)
703 {
704 PINDEX_ROOT_ATTRIBUTE IndexRootAttr;
705
706 IndexRootAttr = (PINDEX_ROOT_ATTRIBUTE)((ULONG_PTR)Attribute + Attribute->Resident.ValueOffset);
707
708 if (IndexRootAttr->AttributeType == AttributeFileName)
709 ASSERT(IndexRootAttr->CollationRule == COLLATION_FILE_NAME);
710
711 DbgPrint(" $INDEX_ROOT (%uB, %u) ", IndexRootAttr->SizeOfEntry, IndexRootAttr->ClustersPerIndexRecord);
712
713 if (IndexRootAttr->Header.Flags == INDEX_ROOT_SMALL)
714 {
715 DbgPrint(" (small) ");
716 }
717 else
718 {
719 ASSERT(IndexRootAttr->Header.Flags == INDEX_ROOT_LARGE);
720 DbgPrint(" (large) ");
721 }
722 }
723
724
725 static
726 VOID
727 NtfsDumpAttribute(PDEVICE_EXTENSION Vcb,
728 PNTFS_ATTR_RECORD Attribute)
729 {
730 UNICODE_STRING Name;
731
732 ULONGLONG lcn = 0;
733 ULONGLONG runcount = 0;
734
735 switch (Attribute->Type)
736 {
737 case AttributeFileName:
738 NtfsDumpFileNameAttribute(Attribute);
739 break;
740
741 case AttributeStandardInformation:
742 NtfsDumpStandardInformationAttribute(Attribute);
743 break;
744
745 case AttributeObjectId:
746 DbgPrint(" $OBJECT_ID ");
747 break;
748
749 case AttributeSecurityDescriptor:
750 DbgPrint(" $SECURITY_DESCRIPTOR ");
751 break;
752
753 case AttributeVolumeName:
754 NtfsDumpVolumeNameAttribute(Attribute);
755 break;
756
757 case AttributeVolumeInformation:
758 NtfsDumpVolumeInformationAttribute(Attribute);
759 break;
760
761 case AttributeData:
762 DbgPrint(" $DATA ");
763 //DataBuf = ExAllocatePool(NonPagedPool,AttributeLengthAllocated(Attribute));
764 break;
765
766 case AttributeIndexRoot:
767 NtfsDumpIndexRootAttribute(Attribute);
768 break;
769
770 case AttributeIndexAllocation:
771 DbgPrint(" $INDEX_ALLOCATION ");
772 break;
773
774 case AttributeBitmap:
775 DbgPrint(" $BITMAP ");
776 break;
777
778 case AttributeReparsePoint:
779 DbgPrint(" $REPARSE_POINT ");
780 break;
781
782 case AttributeEAInformation:
783 DbgPrint(" $EA_INFORMATION ");
784 break;
785
786 case AttributeEA:
787 DbgPrint(" $EA ");
788 break;
789
790 case AttributePropertySet:
791 DbgPrint(" $PROPERTY_SET ");
792 break;
793
794 case AttributeLoggedUtilityStream:
795 DbgPrint(" $LOGGED_UTILITY_STREAM ");
796 break;
797
798 default:
799 DbgPrint(" Attribute %lx ",
800 Attribute->Type);
801 break;
802 }
803
804 if (Attribute->Type != AttributeAttributeList)
805 {
806 if (Attribute->NameLength != 0)
807 {
808 Name.Length = Attribute->NameLength * sizeof(WCHAR);
809 Name.MaximumLength = Name.Length;
810 Name.Buffer = (PWCHAR)((ULONG_PTR)Attribute + Attribute->NameOffset);
811
812 DbgPrint("'%wZ' ", &Name);
813 }
814
815 DbgPrint("(%s)\n",
816 Attribute->IsNonResident ? "non-resident" : "resident");
817
818 if (Attribute->IsNonResident)
819 {
820 FindRun(Attribute,0,&lcn, &runcount);
821
822 DbgPrint(" AllocatedSize %I64u DataSize %I64u InitilizedSize %I64u\n",
823 Attribute->NonResident.AllocatedSize, Attribute->NonResident.DataSize, Attribute->NonResident.InitializedSize);
824 DbgPrint(" logical clusters: %I64u - %I64u\n",
825 lcn, lcn + runcount - 1);
826 }
827 else
828 DbgPrint(" %u bytes of data\n", Attribute->Resident.ValueLength);
829 }
830 }
831
832
833 VOID NtfsDumpDataRunData(PUCHAR DataRun)
834 {
835 UCHAR DataRunOffsetSize;
836 UCHAR DataRunLengthSize;
837 CHAR i;
838
839 DbgPrint("%02x ", *DataRun);
840
841 if (*DataRun == 0)
842 return;
843
844 DataRunOffsetSize = (*DataRun >> 4) & 0xF;
845 DataRunLengthSize = *DataRun & 0xF;
846
847 DataRun++;
848 for (i = 0; i < DataRunLengthSize; i++)
849 {
850 DbgPrint("%02x ", *DataRun);
851 DataRun++;
852 }
853
854 for (i = 0; i < DataRunOffsetSize; i++)
855 {
856 DbgPrint("%02x ", *DataRun);
857 DataRun++;
858 }
859
860 NtfsDumpDataRunData(DataRun);
861 }
862
863
864 VOID
865 NtfsDumpDataRuns(PVOID StartOfRun,
866 ULONGLONG CurrentLCN)
867 {
868 PUCHAR DataRun = StartOfRun;
869 LONGLONG DataRunOffset;
870 ULONGLONG DataRunLength;
871
872 if (CurrentLCN == 0)
873 {
874 DPRINT1("Dumping data runs.\n\tData:\n\t\t");
875 NtfsDumpDataRunData(StartOfRun);
876 DbgPrint("\n\tRuns:\n\t\tOff\t\tLCN\t\tLength\n");
877 }
878
879 DataRun = DecodeRun(DataRun, &DataRunOffset, &DataRunLength);
880
881 if (DataRunOffset != -1)
882 CurrentLCN += DataRunOffset;
883
884 DbgPrint("\t\t%I64d\t", DataRunOffset);
885 if (DataRunOffset < 99999)
886 DbgPrint("\t");
887 DbgPrint("%I64u\t", CurrentLCN);
888 if (CurrentLCN < 99999)
889 DbgPrint("\t");
890 DbgPrint("%I64u\n", DataRunLength);
891
892 if (*DataRun == 0)
893 DbgPrint("\t\t00\n");
894 else
895 NtfsDumpDataRuns(DataRun, CurrentLCN);
896 }
897
898
899 VOID
900 NtfsDumpFileAttributes(PDEVICE_EXTENSION Vcb,
901 PFILE_RECORD_HEADER FileRecord)
902 {
903 NTSTATUS Status;
904 FIND_ATTR_CONTXT Context;
905 PNTFS_ATTR_RECORD Attribute;
906
907 Status = FindFirstAttribute(&Context, Vcb, FileRecord, FALSE, &Attribute);
908 while (NT_SUCCESS(Status))
909 {
910 NtfsDumpAttribute(Vcb, Attribute);
911
912 Status = FindNextAttribute(&Context, &Attribute);
913 }
914
915 FindCloseAttribute(&Context);
916 }
917
918 PFILENAME_ATTRIBUTE
919 GetFileNameFromRecord(PDEVICE_EXTENSION Vcb,
920 PFILE_RECORD_HEADER FileRecord,
921 UCHAR NameType)
922 {
923 FIND_ATTR_CONTXT Context;
924 PNTFS_ATTR_RECORD Attribute;
925 PFILENAME_ATTRIBUTE Name;
926 NTSTATUS Status;
927
928 Status = FindFirstAttribute(&Context, Vcb, FileRecord, FALSE, &Attribute);
929 while (NT_SUCCESS(Status))
930 {
931 if (Attribute->Type == AttributeFileName)
932 {
933 Name = (PFILENAME_ATTRIBUTE)((ULONG_PTR)Attribute + Attribute->Resident.ValueOffset);
934 if (Name->NameType == NameType ||
935 (Name->NameType == NTFS_FILE_NAME_WIN32_AND_DOS && NameType == NTFS_FILE_NAME_WIN32) ||
936 (Name->NameType == NTFS_FILE_NAME_WIN32_AND_DOS && NameType == NTFS_FILE_NAME_DOS))
937 {
938 FindCloseAttribute(&Context);
939 return Name;
940 }
941 }
942
943 Status = FindNextAttribute(&Context, &Attribute);
944 }
945
946 FindCloseAttribute(&Context);
947 return NULL;
948 }
949
950 /**
951 * GetPackedByteCount
952 * Returns the minimum number of bytes needed to represent the value of a
953 * 64-bit number. Used to encode data runs.
954 */
955 UCHAR
956 GetPackedByteCount(LONGLONG NumberToPack,
957 BOOLEAN IsSigned)
958 {
959 int bytes = 0;
960 if (!IsSigned)
961 {
962 if (NumberToPack >= 0x0100000000000000)
963 return 8;
964 if (NumberToPack >= 0x0001000000000000)
965 return 7;
966 if (NumberToPack >= 0x0000010000000000)
967 return 6;
968 if (NumberToPack >= 0x0000000100000000)
969 return 5;
970 if (NumberToPack >= 0x0000000001000000)
971 return 4;
972 if (NumberToPack >= 0x0000000000010000)
973 return 3;
974 if (NumberToPack >= 0x0000000000000100)
975 return 2;
976 return 1;
977 }
978
979 if (NumberToPack > 0)
980 {
981 // we have to make sure the number that gets encoded won't be interpreted as negative
982 if (NumberToPack >= 0x0080000000000000)
983 return 8;
984 if (NumberToPack >= 0x0000800000000000)
985 return 7;
986 if (NumberToPack >= 0x0000008000000000)
987 return 6;
988 if (NumberToPack >= 0x0000000080000000)
989 return 5;
990 if (NumberToPack >= 0x0000000000800000)
991 return 4;
992 if (NumberToPack >= 0x0000000000008000)
993 return 3;
994 if (NumberToPack >= 0x0000000000000080)
995 return 2;
996 return 1;
997 }
998 else
999 {
1000 // negative number
1001 if (NumberToPack <= 0xff80000000000000)
1002 return 8;
1003 if (NumberToPack <= 0xffff800000000000)
1004 return 7;
1005 if (NumberToPack <= 0xffffff8000000000)
1006 return 6;
1007 if (NumberToPack <= 0xffffffff80000000)
1008 return 5;
1009 if (NumberToPack <= 0xffffffffff800000)
1010 return 4;
1011 if (NumberToPack <= 0xffffffffffff8000)
1012 return 3;
1013 if (NumberToPack <= 0xffffffffffffff80)
1014 return 2;
1015 return 1;
1016 }
1017 return bytes;
1018 }
1019
1020 NTSTATUS
1021 GetLastClusterInDataRun(PDEVICE_EXTENSION Vcb, PNTFS_ATTR_RECORD Attribute, PULONGLONG LastCluster)
1022 {
1023 LONGLONG DataRunOffset;
1024 ULONGLONG DataRunLength;
1025 LONGLONG DataRunStartLCN;
1026
1027 ULONGLONG LastLCN = 0;
1028 PUCHAR DataRun = (PUCHAR)Attribute + Attribute->NonResident.MappingPairsOffset;
1029
1030 if (!Attribute->IsNonResident)
1031 return STATUS_INVALID_PARAMETER;
1032
1033 while (1)
1034 {
1035 DataRun = DecodeRun(DataRun, &DataRunOffset, &DataRunLength);
1036
1037 if (DataRunOffset != -1)
1038 {
1039 // Normal data run.
1040 DataRunStartLCN = LastLCN + DataRunOffset;
1041 LastLCN = DataRunStartLCN;
1042 *LastCluster = LastLCN + DataRunLength - 1;
1043 }
1044
1045 if (*DataRun == 0)
1046 break;
1047 }
1048
1049 return STATUS_SUCCESS;
1050 }
1051
1052 PSTANDARD_INFORMATION
1053 GetStandardInformationFromRecord(PDEVICE_EXTENSION Vcb,
1054 PFILE_RECORD_HEADER FileRecord)
1055 {
1056 NTSTATUS Status;
1057 FIND_ATTR_CONTXT Context;
1058 PNTFS_ATTR_RECORD Attribute;
1059 PSTANDARD_INFORMATION StdInfo;
1060
1061 Status = FindFirstAttribute(&Context, Vcb, FileRecord, FALSE, &Attribute);
1062 while (NT_SUCCESS(Status))
1063 {
1064 if (Attribute->Type == AttributeStandardInformation)
1065 {
1066 StdInfo = (PSTANDARD_INFORMATION)((ULONG_PTR)Attribute + Attribute->Resident.ValueOffset);
1067 FindCloseAttribute(&Context);
1068 return StdInfo;
1069 }
1070
1071 Status = FindNextAttribute(&Context, &Attribute);
1072 }
1073
1074 FindCloseAttribute(&Context);
1075 return NULL;
1076 }
1077
1078 PFILENAME_ATTRIBUTE
1079 GetBestFileNameFromRecord(PDEVICE_EXTENSION Vcb,
1080 PFILE_RECORD_HEADER FileRecord)
1081 {
1082 PFILENAME_ATTRIBUTE FileName;
1083
1084 FileName = GetFileNameFromRecord(Vcb, FileRecord, NTFS_FILE_NAME_POSIX);
1085 if (FileName == NULL)
1086 {
1087 FileName = GetFileNameFromRecord(Vcb, FileRecord, NTFS_FILE_NAME_WIN32);
1088 if (FileName == NULL)
1089 {
1090 FileName = GetFileNameFromRecord(Vcb, FileRecord, NTFS_FILE_NAME_DOS);
1091 }
1092 }
1093
1094 return FileName;
1095 }
1096
1097 /* EOF */