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