NT4/private/sdktools/ntfsdump/ntfsdump.cxx
2020-09-30 17:12:29 +02:00

1035 lines
30 KiB
C++

#define _NTAPI_ULIB_
#include "ulib.hxx"
#include "ulibcl.hxx"
#include "error.hxx"
#include "smsg.hxx"
#include "wstring.hxx"
#include "hmem.hxx"
#include "drive.hxx"
#include "secrun.hxx"
#include "ifssys.hxx"
#include "numset.hxx"
#include "untfs.hxx"
#include "ntfssa.hxx"
#include "ntfsbit.hxx"
#include "upcase.hxx"
#include "mft.hxx"
#include "mftfile.hxx"
#include "frs.hxx"
#include "bitfrs.hxx"
#include "upfile.hxx"
#include "attrib.hxx"
#include "attrrec.hxx"
#include "attrlist.hxx"
extern "C" {
#include <stdio.h>
#include <stdlib.h>
}
//
// Global variables to support DumpAttrib
//
char *AttribType, *AttribName;
VOID
Usage(
)
{
printf("usage: ntfsdump <drive> /file <filename> options\n");
printf(" ntfsdump <drive> /frs <file number> options\n");
printf(" ntfsdump <drive> /boot\n" );
printf("\n" );
printf("options:\n" );
printf(" /records display attribute records.\n");
printf(" /index display index information.\n");
printf(" /entries display index entries.\n" );
printf(" /list display attribute list (if present)\n");
printf(" /children display child FRS's\n");
printf(" /all display all of the above\n");
printf(" /attrib <type> <name> display attribute contents\n");
}
VOID
DumpBoot(
PLOG_IO_DP_DRIVE Drive
)
{
HMEM Hmem;
SECRUN Secrun;
PPACKED_BOOT_SECTOR p;
BIOS_PARAMETER_BLOCK bpb;
if( !Hmem.Initialize() ||
!Secrun.Initialize( &Hmem, Drive, 0, 1 ) ) {
printf( "Insufficient memory to dump boot sector.\n" );
return;
}
if( !Secrun.Read() ) {
printf( "Cannot read boot sector.\n" );
return;
}
printf( "***** BOOT SECTOR *****\n\n" );
p = (PPACKED_BOOT_SECTOR)(Secrun.GetBuf());
UnpackBios(&bpb, &(p->PackedBpb));
printf( "OEM String: %c%c%c%c%c%c%c%c\n",
p->Oem[0],
p->Oem[1],
p->Oem[2],
p->Oem[3],
p->Oem[4],
p->Oem[5],
p->Oem[6],
p->Oem[7]);
printf( "Bytes per sector: %8x\n", bpb.BytesPerSector );
printf( "Sectors per cluster: %8x\n", bpb.SectorsPerCluster);
printf( "Reserved Sectors: %8x\n", bpb.ReservedSectors);
printf( "Number of fats: %8x\n", bpb.Fats);
printf( "Root entries: %8x\n", bpb.RootEntries);
printf( "Small sector count: %8x\n", bpb.Sectors);
printf( "Media byte: %8x\n", bpb.Media);
printf( "Sectors per fat: %8x\n", bpb.SectorsPerFat);
printf( "Sectors per track: %8x\n", bpb.SectorsPerTrack);
printf( "Number of heads: %8x\n", bpb.Heads);
printf( "Number of hidden sectors: %8x\n", bpb.HiddenSectors);
printf( "Large number of sectors: %8x\n", bpb.LargeSectors);
printf( "Physical drive: %8x\n", p->PhysicalDrive);
printf( "NTFS number of sectors: %8x\n", p->NumberSectors.LowPart);
printf( "MFT starting cluster: %8x\n", p->MftStartLcn.GetLowPart());
printf( "MFT mirror starting cluster: %8x\n", p->Mft2StartLcn.GetLowPart());
printf( "Clusters per file record: %8x\n", p->ClustersPerFileRecordSegment);
printf( "Clusters per index block: %8x\n", p->DefaultClustersPerIndexAllocationBuffer);
printf( "SerialNumber: %08x%08x\n", p->SerialNumber.HighPart,
p->SerialNumber.LowPart);
printf( "Checksum: %8x\n\n", p->Checksum);
}
BOOLEAN
PostReadMultiSectorFixup(
IN OUT PVOID MultiSectorBuffer,
IN ULONG BufferSize
)
/*++
Routine Description:
This routine first verifies that the first element of the
update sequence array is written at the end of every
SEQUENCE_NUMBER_STRIDE bytes. If not, then this routine
returns FALSE.
Otherwise this routine swaps the following elements in the
update sequence array into the appropriate positions in the
multi sector buffer.
This routine will also check to make sure that the update
sequence array is valid and that the BufferSize is appropriate
for this size of update sequence array. Otherwise, this
routine will not update the array sequence and return TRUE.
Arguments:
MultiSectorBuffer - Supplies the buffer to be updated.
BufferSize - Supplies the number of bytes in this
buffer.
Return Value:
FALSE - The last write to this buffer failed.
TRUE - There is no evidence that this buffer is bad.
--*/
{
PUNTFS_MULTI_SECTOR_HEADER pheader;
USHORT i, size, offset;
PUPDATE_SEQUENCE_NUMBER parray, pnumber;
pheader = (PUNTFS_MULTI_SECTOR_HEADER) MultiSectorBuffer;
size = pheader->UpdateSequenceArraySize;
offset = pheader->UpdateSequenceArrayOffset;
if (BufferSize%SEQUENCE_NUMBER_STRIDE ||
offset%sizeof(UPDATE_SEQUENCE_NUMBER) ||
offset + size*sizeof(UPDATE_SEQUENCE_NUMBER) > BufferSize ||
BufferSize/SEQUENCE_NUMBER_STRIDE + 1 != size) {
return TRUE;
}
parray = (PUPDATE_SEQUENCE_NUMBER) ((PCHAR) pheader + offset);
for (i = 1; i < size; i++) {
pnumber = (PUPDATE_SEQUENCE_NUMBER)
((PCHAR) pheader + (i*SEQUENCE_NUMBER_STRIDE -
sizeof(UPDATE_SEQUENCE_NUMBER)));
if (*pnumber != parray[0]) {
return FALSE;
}
*pnumber = parray[i];
}
return TRUE;
}
DECLARE_CLASS( NTFS_SA );
BOOLEAN
SetupVolume(
IN OUT PLOG_IO_DP_DRIVE Drive,
IN OUT PMESSAGE Message,
OUT PNTFS_SA NtfsSa,
OUT PNTFS_MFT_FILE Mft,
OUT PNTFS_BITMAP VolumeBitmap,
OUT PNTFS_UPCASE_TABLE UpcaseTable
)
{
NTFS_BITMAP_FILE bitmap_file;
NTFS_ATTRIBUTE bitmap_attribute;
NTFS_UPCASE_FILE upcase_file;
NTFS_ATTRIBUTE upcase_attribute;
BOOLEAN error;
if (!NtfsSa->Initialize(Drive, Message)) {
return FALSE;
}
if (!NtfsSa->Read())
return FALSE;
if (!VolumeBitmap->Initialize(Drive->QuerySectors()/
(ULONG) NtfsSa->QueryClusterFactor(), FALSE))
return FALSE;
if (!Mft->Initialize(Drive,
NtfsSa->QueryMftStartingLcn(),
NtfsSa->QueryClusterFactor(),
NtfsSa->QueryClustersPerFrs(),
NtfsSa->QueryVolumeSectors(),
VolumeBitmap,
NULL))
return FALSE;
if (!Mft->Read())
return FALSE;
if (!bitmap_file.Initialize(Mft->GetMasterFileTable()))
return FALSE;
if (!bitmap_file.Read())
return FALSE;
if (!bitmap_file.QueryAttribute(&bitmap_attribute, &error, $DATA))
return FALSE;
if (!VolumeBitmap->Read(&bitmap_attribute))
return FALSE;
if (!upcase_file.Initialize(Mft->GetMasterFileTable()))
return FALSE;
if (!upcase_file.Read())
return FALSE;
if (!upcase_file.QueryAttribute(&upcase_attribute, &error, $DATA))
return FALSE;
if (!UpcaseTable->Initialize(&upcase_attribute))
return FALSE;
Mft->SetUpcaseTable( UpcaseTable );
Mft->GetMasterFileTable()->SetUpcaseTable( UpcaseTable );
return TRUE;
}
VOID
DumpFrs(
IN BIG_INT FileNumber,
IN OUT PNTFS_MFT_FILE MftFile,
IN BOOLEAN DumpRecords,
IN BOOLEAN DumpAttributeList,
IN BOOLEAN DumpChildren,
IN BOOLEAN DumpAttrib,
IN BOOLEAN IsChild
)
{
HMEM hmem;
NTFS_FRS_STRUCTURE frs_struc;
NTFS_ATTRIBUTE_LIST attr_list;
NTFS_ATTRIBUTE_RECORD attrrec;
NUMBER_SET children;
PCFILE_RECORD_SEGMENT_HEADER pfrs;
PATTRIBUTE_RECORD_HEADER prec;
DSTRING record_name;
PSTR pstr;
VCN current_vcn;
LCN current_lcn;
ULONG i, j;
// First, initialize and read this single FRS.
//
if( !hmem.Initialize() ||
!frs_struc.Initialize( &hmem,
MftFile->GetMasterFileTable()->GetDataAttribute(),
FileNumber,
MftFile->QueryClusterFactor(),
MftFile->QueryClustersPerFrs(),
MftFile->QueryVolumeSectors(),
MftFile->GetUpcaseTable() ) ||
!frs_struc.Read() ) {
printf( "Can't read FRS %d (0x%x)\n", FileNumber, FileNumber );
}
printf( "***** %sFILE RECORD SEGMENT 0x%x *****\n\n",
IsChild ? "CHILD " : "",
FileNumber );
printf( "Sectors in FRS: " );
for( current_vcn = FileNumber * MftFile->QueryClustersPerFrs(), i = 0;
i < MftFile->QueryClustersPerFrs();
i++, current_vcn += 1 ) {
if( !MftFile->GetMasterFileTable()->
GetDataAttribute()->
QueryLcnFromVcn( current_vcn, &current_lcn ) ) {
printf( " - Invalid FRS number.\n" );
return;
}
for( j = 0; j < MftFile->QueryClusterFactor(); j++ ) {
printf( " 0x%x", current_lcn.GetLowPart() * MftFile->QueryClusterFactor() + j );
}
}
printf( "\n\n" );
pfrs = (PCFILE_RECORD_SEGMENT_HEADER)hmem.GetBuf();
printf( "Signature: %c%c%c%c\n",
pfrs->MultiSectorHeader.Signature[0],
pfrs->MultiSectorHeader.Signature[1],
pfrs->MultiSectorHeader.Signature[2],
pfrs->MultiSectorHeader.Signature[3]);
printf( "Update sequence array offset: %x\n",
pfrs->MultiSectorHeader.UpdateSequenceArrayOffset);
printf( "Update sequence array size: %x\n",
pfrs->MultiSectorHeader.UpdateSequenceArraySize);
printf( "Sequence number: %x\n", pfrs->SequenceNumber);
printf( "Reference count: %x\n", pfrs->ReferenceCount);
printf( "First attribute offset: %x\n", pfrs->FirstAttributeOffset);
printf( "Flags: %x\n", pfrs->Flags);
printf( "First free byte: %x\n", pfrs->FirstFreeByte);
printf( "Bytes available: %x\n", pfrs->BytesAvailable);
printf( "Base file record segment (FRS, SeqNum): %x, %x\n",
pfrs->BaseFileRecordSegment.LowPart,
pfrs->BaseFileRecordSegment.SequenceNumber);
printf( "Next attribute instance: %x\n", pfrs->NextAttributeInstance);
printf( "\n" );
// Now enumerate all of the attribute records.
//
prec = NULL;
while (DumpRecords &&
(prec = (PATTRIBUTE_RECORD_HEADER) frs_struc.GetNextAttributeRecord(prec))) {
if (!attrrec.Initialize(prec) ||
!attrrec.QueryName(&record_name) ||
!(pstr = record_name.QuerySTR())) {
return;
}
printf( "ATTRIBUTE RECORD at offset %x\n", (PCHAR) prec - (PCHAR) pfrs);
printf( " Type code, name: %x, %s\n", prec->TypeCode, pstr);
printf( " Record length: %x\n", prec->RecordLength);
printf( " Form code: %x\n", prec->FormCode);
printf( " Name length: %x\n", prec->NameLength);
printf( " Name offset: %x\n", prec->NameOffset);
printf( " Flags: %x\n", prec->Flags);
printf( " Instance: %x\n", prec->Instance);
if (prec->FormCode & NONRESIDENT_FORM) {
printf( " Lowest vcn: %x\n",
prec->Form.Nonresident.LowestVcn.LowPart);
printf( " Highest vcn: %x\n",
prec->Form.Nonresident.HighestVcn.LowPart);
printf( " Mapping pairs offset: %x\n",
prec->Form.Nonresident.MappingPairsOffset);
printf( " Allocated length: %x\n",
prec->Form.Nonresident.AllocatedLength.LowPart);
printf( " File size: %x\n",
prec->Form.Nonresident.FileSize.LowPart);
printf( " Valid data length: %x\n",
prec->Form.Nonresident.ValidDataLength.LowPart);
NTFS_EXTENT_LIST extents;
BIG_INT vcn, lcn, run_length;
ULONG i;
if (!attrrec.QueryExtentList(&extents)) {
return;
}
for (i = 0; i < extents.QueryNumberOfExtents(); i++) {
if (!extents.QueryExtent(i, &vcn, &lcn, &run_length)) {
break;
}
printf( " (vcn, lcn, run length): (%x, %x, %x)\n",
vcn.GetLowPart(),
lcn.GetLowPart(),
run_length.GetLowPart());
}
} else {
printf( " Value length: %x\n", prec->Form.Resident.ValueLength);
printf( " Value offset: %x\n", prec->Form.Resident.ValueOffset);
printf( " Resident flags: %x\n", prec->Form.Resident.ResidentFlags);
}
DELETE(pstr);
printf( "\n" );
}
if( (DumpAttributeList || DumpChildren ) &&
!IsChild &&
frs_struc.IsInUse() &&
frs_struc.QueryAttributeList( &attr_list ) &&
attr_list.ReadList() ) {
ULONG Type;
VCN LowestVcn;
MFT_SEGMENT_REFERENCE SegmentReference;
USHORT InstanceTag;
DSTRING Name;
PSTR name_string;
// This frs has an Attribute List--dump it and build up
// a list of child FRS's.
//
if( DumpAttributeList ) {
printf( "***** ATTRIBUTE LIST FOR FRS 0x%x *****\n", FileNumber );
}
if( !children.Initialize() ) {
printf( "Insufficient memory.\n" );
exit(1);
}
for( i = 0;
attr_list.QueryEntry( i, &Type, &LowestVcn,
&SegmentReference, &InstanceTag, &Name );
i++ ) {
name_string = Name.QuerySTR();
if( DumpAttributeList ) {
printf( "Attribute List Entry %d\n", i );
printf( " Type: %x\n", Type );
printf( " LowestVcn: %x\n", LowestVcn.GetLowPart() );
printf( " Segment Reference (FRS, SeqNum): %x,%x\n",
SegmentReference.LowPart,
SegmentReference.SequenceNumber );
printf( " Instance Tag: %x\n", InstanceTag );
printf( " Name: %s\n", name_string );
}
if( SegmentReference.LowPart != FileNumber ) {
children.Add( SegmentReference.LowPart );
}
DELETE( name_string );
}
if( DumpChildren ) {
for( i = 0; i < children.QueryCardinality(); i++ ) {
DumpFrs( children.QueryNumber( i ).GetLowPart(),
MftFile,
DumpRecords,
FALSE,
FALSE,
FALSE,
TRUE );
}
}
}
if ( DumpAttrib ) {
DSTRING attr_name;
PCWSTRING pcAttrName;
ULONG attr_type;
NTFS_FILE_RECORD_SEGMENT frs;
NTFS_ATTRIBUTE attr;
BOOLEAN error;
ULONG length;
PUCHAR data;
if (0 == strlen(AttribName)) {
pcAttrName = NULL;
} else {
if (!attr_name.Initialize(AttribName)) {
printf("Cannot init attr_name\n");
return;
}
pcAttrName = &attr_name;
}
sscanf(AttribType, "%x", &attr_type);
printf( "***** ATTRIBUTE VALUE frs 0x%x, type 0x%x, name \"%s\" *****\n\n",
FileNumber.GetLowPart(), attr_type,
(NULL == AttribName) ? "" : AttribName);
if (!frs.Initialize((VCN)FileNumber, MftFile) ||
!frs.Read()) {
printf("Cannot init or read frs\n");
return;
}
if (!frs.QueryAttribute(&attr, &error, attr_type, pcAttrName)) {
printf("Cannot QueryAttribute\n");
return;
}
// Read
ULONG bytes_read;
length = attr.QueryValueLength().GetLowPart();
if (attr.QueryValueLength().GetHighPart() != 0 ||
(NULL == (data = (PUCHAR)MALLOC(length + 16)))) {
printf("Cannot allocate data for attribute\n");
return;
}
if (!attr.Read(data, 0, length, &bytes_read) ||
bytes_read != length) {
printf("Cannot read attribute\n");
return;
}
// Display
ULONG pos = 0;
while (pos < length) {
printf("%05X ", pos);
for (i = 0; i < 16; ++i) {
if (8 == i)
printf(" ");
printf("%02X ", data[pos + i]);
}
printf(" ");
for (i = 0; i < 16; ++i) {
printf("%c", isprint(data[pos + i]) ? data[pos + i] : '.');
}
pos += 16;
printf("\n");
}
}
}
VOID
DumpIndexHeaderAndEntries(
PINDEX_HEADER IndexHeader,
ULONG Size,
BOOLEAN DumpEntries
)
{
PINDEX_ENTRY CurrentEntry;
PFILE_NAME FileName;
ULONG i, CurrentOffset;
printf( "INDEX HEADER\n" );
printf( " FirstIndexEntry: %x\n", IndexHeader->FirstIndexEntry );
printf( " FirstFreeByte: %x\n", IndexHeader->FirstFreeByte );
printf( " BytesAvailable: %x\n", IndexHeader->BytesAvailable );
printf( " Flags:" );
if( IndexHeader->Flags & INDEX_NODE ) {
printf( " INDEX_NODE");
}
printf( "\n\n" );
CurrentEntry = (PINDEX_ENTRY)((PBYTE)IndexHeader +
IndexHeader->FirstIndexEntry );
CurrentOffset = IndexHeader->FirstIndexEntry;
while( DumpEntries ) {
if( CurrentOffset + sizeof(INDEX_ENTRY) > Size ||
CurrentEntry->Length < sizeof( INDEX_ENTRY ) ||
CurrentOffset + CurrentEntry->Length > Size ) {
printf( " INVALID ENTRY.\n" );
break;
}
printf( " File Reference (FRS, Seq No): %x, %x\n",
CurrentEntry->FileReference.LowPart,
CurrentEntry->FileReference.SequenceNumber );
printf( " Length: %x\n", CurrentEntry->Length );
printf( " Value Length: %x\n", CurrentEntry->AttributeLength );
printf( " Flags:" );
if( CurrentEntry->Flags & INDEX_ENTRY_NODE ) {
printf( " INDEX_ENTRY_NODE" );
}
if( CurrentEntry->Flags & INDEX_ENTRY_END ) {
printf( " INDEX_ENTRY_END" );
}
printf( "\n" );
if( CurrentEntry->Flags & INDEX_ENTRY_END ) {
if( CurrentEntry->Flags & INDEX_ENTRY_NODE ) {
printf( " Downpointer: %x\n", (GetDownpointer(CurrentEntry)).GetLowPart() );
}
break;
}
// This had better be a file name attribute, since
// that's how I'll display it.
//
FileName = (PFILE_NAME)( (PBYTE)CurrentEntry + sizeof(INDEX_ENTRY) );
if( CurrentOffset + sizeof(INDEX_ENTRY) + sizeof(FILE_NAME) > Size ||
CurrentOffset + sizeof(INDEX_ENTRY) + FileName->FileNameLength*sizeof(WCHAR) > Size ) {
printf( " INVALID FILE NAME.\n" );
break;
}
printf( " Parent Directory (FRS, Seq No): %x, %x\n",
FileName->ParentDirectory.LowPart,
FileName->ParentDirectory.SequenceNumber );
printf( " Allocated Length: %x\n", FileName->Info.AllocatedLength.GetLowPart() );
printf( " File Size: %x\n", FileName->Info.FileSize.GetLowPart() );
printf( " Name Length: %x\n", FileName->FileNameLength );
printf( " Flags:" );
if( FileName->Flags & FILE_NAME_DOS ) {
printf( " FILE_NAME_DOS" );
}
if( FileName->Flags & FILE_NAME_NTFS ) {
printf( " FILE_NAME_NTFS" );
}
printf( "\n" );
printf( " File name: " );
for( i = 0; i < FileName->FileNameLength; i++ ) {
printf( "%c", FileName->FileName[i] );
}
printf( "\n" );
if( CurrentEntry->Flags & INDEX_ENTRY_NODE ) {
printf( " Downpointer: %x\n", (GetDownpointer(CurrentEntry)).GetLowPart() );
}
printf( "\n" );
CurrentOffset += CurrentEntry->Length;
CurrentEntry = GetNextEntry( CurrentEntry );
}
}
VOID
DumpIndex(
IN BIG_INT FileNumber,
IN OUT PNTFS_MASTER_FILE_TABLE Mft,
IN BOOLEAN DumpEntries
)
{
NTFS_FILE_RECORD_SEGMENT Frs;
NTFS_ATTRIBUTE IndexRootAttribute, IndexAllocationAttribute;
HMEM Hmem;
BOOLEAN Error;
DSTRING IndexName;
PINDEX_ROOT Root;
PINDEX_ALLOCATION_BUFFER BufferData;
VCN Vcn, current_vcn, current_lcn;
ULONG BufferCount, BytesRead, i, j, k;
if( !IndexName.Initialize( FileNameIndexNameData ) ) {
printf( "Insufficient memory.\n" );
return;
}
if( !Frs.Initialize( FileNumber, Mft ) ||
!Frs.Read() ) {
printf( "Can't read FRS %d (0x%x)\n", FileNumber, FileNumber );
return;
}
if( !Frs.IsAttributePresent( $INDEX_ROOT, &IndexName ) ) {
printf( "This FRS is not an index.\n" );
return;
}
if( !Frs.QueryAttribute( &IndexRootAttribute,
&Error,
$INDEX_ROOT,
&IndexName ) ||
!IndexRootAttribute.IsResident() ) {
printf( "Cannot fetch index root.\n" );
return;
}
Root = (PINDEX_ROOT)IndexRootAttribute.GetResidentValue();
printf( "\n" );
printf( "***** INDEX ROOT in FRS 0x%x *****\n\n", FileNumber );
printf( "Indexed Attribute Type: %x\n", Root->IndexedAttributeType );
printf( "Collation Rule: %x\n", Root->CollationRule );
printf( "Bytes per Index Buffer: %x\n", Root->BytesPerIndexBuffer );
printf( "Clusters per Buffer: %x\n", Root->ClustersPerIndexBuffer );
printf( "\n" );
DumpIndexHeaderAndEntries( &Root->IndexHeader,
IndexRootAttribute.QueryValueLength().GetLowPart() - FIELD_OFFSET(INDEX_ROOT, IndexHeader),
DumpEntries );
printf( "\n" );
if( !Frs.IsAttributePresent( $INDEX_ALLOCATION, &IndexName ) ) {
// That's all!
//
return;
}
if( !Frs.QueryAttribute( &IndexAllocationAttribute,
&Error,
$INDEX_ALLOCATION,
&IndexName ) ) {
printf( "Can't fetch index allocation attribute.\n" );
return;
}
BufferCount = (IndexAllocationAttribute.QueryValueLength()/Root->BytesPerIndexBuffer).GetLowPart();
if( !Hmem.Initialize() || !Hmem.Acquire( Root->BytesPerIndexBuffer ) ) {
printf( "Insufficient memory.\n" );
return;
}
BufferData = (PINDEX_ALLOCATION_BUFFER)Hmem.GetBuf();
for( k = 0; k < BufferCount; k++ ) {
Vcn = k * Root->ClustersPerIndexBuffer;
printf( "***** INDEX BUFFER at VCN 0x%x *****\n\n", Vcn.GetLowPart() );
printf( "Sectors in Index Allocation Block: " );
for( current_vcn = Vcn, i = 0;
i < Root->ClustersPerIndexBuffer;
i++, current_vcn += 1 ) {
if( !IndexAllocationAttribute.
QueryLcnFromVcn( current_vcn, &current_lcn ) ) {
printf( " - Invalid buffer number.\n" );
return;
}
for( j = 0; j < Mft->QueryClusterFactor(); j++ ) {
printf( " 0x%x", current_lcn.GetLowPart() * Mft->QueryClusterFactor() + j );
}
}
printf( "\n" );
if( !IndexAllocationAttribute.Read( BufferData,
k * Root->BytesPerIndexBuffer,
Root->BytesPerIndexBuffer,
&BytesRead ) ||
BytesRead != Root->BytesPerIndexBuffer ) {
printf( "Can't read index buffer.\n\n" );
continue;
}
PostReadMultiSectorFixup( BufferData, Root->BytesPerIndexBuffer );
printf( "Signature: %c%c%c%c\n",
BufferData->MultiSectorHeader.Signature[0],
BufferData->MultiSectorHeader.Signature[1],
BufferData->MultiSectorHeader.Signature[2],
BufferData->MultiSectorHeader.Signature[3]);
printf( "Update sequence array offset: %x\n",
BufferData->MultiSectorHeader.UpdateSequenceArrayOffset);
printf( "Update sequence array size: %x\n",
BufferData->MultiSectorHeader.UpdateSequenceArraySize);
printf( "This VCN: %x\n\n", BufferData->ThisVcn.GetLowPart() );
DumpIndexHeaderAndEntries( &BufferData->IndexHeader,
Root->BytesPerIndexBuffer -
FIELD_OFFSET( INDEX_ALLOCATION_BUFFER, IndexHeader ),
DumpEntries );
printf( "\n" );
}
}
void _CRTAPI1
main(
int argc,
char **argv
)
{
LOG_IO_DP_DRIVE Drive;
STREAM_MESSAGE Message;
NTFS_SA NtfsSa;
NTFS_MFT_FILE MftFile;
NTFS_BITMAP VolumeBitmap;
NTFS_UPCASE_TABLE UpcaseTable;
NTFS_FILE_RECORD_SEGMENT Frs;
DSTRING DosDriveName, NtDriveName, FileName;
BOOLEAN BootFlag = FALSE,
RecordsFlag = FALSE,
IndexFlag = FALSE,
EntriesFlag = FALSE,
ListFlag = FALSE,
ChildrenFlag = FALSE,
FileFlag = FALSE,
FrsFlag = FALSE,
AttribFlag = FALSE,
Error;
int i, result, FileNumber;
if( !Message.Initialize( Get_Standard_Output_Stream(),
Get_Standard_Input_Stream() ) ) {
printf( "Error initializing application.\n" );
exit(1);
}
if( argc < 2 || !_stricmp( argv[1], "/?") ) {
Usage();
exit(0);
}
if( !DosDriveName.Initialize( argv[1] ) ) {
printf( "Insufficient memory.\n" );
exit(1);
}
if (!IFS_SYSTEM::DosDriveNameToNtDriveName(&DosDriveName, &NtDriveName)) {
printf( "Invalid drive -- %s\n", argv[1] );
exit(1);
}
if( !Drive.Initialize( &NtDriveName, &Message ) ) {
exit(1);
}
for( i = 2; i < argc; i++ ) {
if( !_stricmp( argv[i], "/boot" ) ) {
BootFlag = TRUE;
continue;
}
if( !_stricmp( argv[i], "/records" ) ) {
RecordsFlag = TRUE;
continue;
}
if( !_stricmp( argv[i], "/index" ) ) {
IndexFlag = TRUE;
continue;
}
if( !_stricmp( argv[i], "/entries" ) ) {
EntriesFlag = TRUE;
continue;
}
if( !_stricmp( argv[i], "/list" ) ) {
ListFlag = TRUE;
continue;
}
if( !_stricmp( argv[i], "/children" ) ) {
ChildrenFlag = TRUE;
continue;
}
if ( !_stricmp( argv[i], "/attrib" ) ) {
AttribFlag = TRUE;
AttribType = argv[i + 1];
AttribName = argv[i + 2];
i += 2;
continue;
}
if( !_stricmp( argv[i], "/all" ) ) {
RecordsFlag = TRUE;
ListFlag = TRUE;
ChildrenFlag = TRUE;
IndexFlag = TRUE;
EntriesFlag = TRUE;
continue;
}
if( !_stricmp( argv[i], "/file" ) ) {
i++;
if( !FileName.Initialize( argv[i] ) ) {
printf( "Insufficient memory.\n" );
exit(1);
}
FileFlag = TRUE;
continue;
}
if( !_stricmp( argv[i], "/frs" ) ) {
i++;
result = sscanf( argv[i], "%i", &FileNumber );
if( result == 0 || result == EOF ) {
printf( "Invalid FRS number.\n" );
exit(1);
}
FrsFlag = TRUE;
continue;
}
printf( "Invalid parameter -- %s\n", argv[i] );
exit(1);
}
if( FileFlag && FrsFlag ) {
printf( "Invalid combination of parameters.\n" );
exit(1);
}
if( BootFlag ) {
DumpBoot( &Drive );
}
if( FileFlag || FrsFlag ) {
if( !SetupVolume( &Drive, &Message,
&NtfsSa, &MftFile, &VolumeBitmap, &UpcaseTable ) ) {
exit(1);
}
if( FileFlag ) {
if( !NtfsSa.QueryFrsFromPath(&FileName,
MftFile.GetMasterFileTable(),
&VolumeBitmap,
&Frs,
&Error) ) {
exit(1);
}
FileNumber = Frs.QueryFileNumber().GetLowPart();
} else {
// the FRS to dump was specified by number.
//
if( !Frs.Initialize( FileNumber, &MftFile ) ||
!Frs.Read() ) {
printf( "Can't read FRS number %d (0x%x)\n", FileNumber, FileNumber );
exit(1);
}
}
printf( "Dump FRS number %d (0x%x)\n", FileNumber, FileNumber );
DumpFrs( FileNumber, &MftFile, RecordsFlag, ListFlag, ChildrenFlag, AttribFlag, FALSE );
if( IndexFlag ) {
DumpIndex( FileNumber, MftFile.GetMasterFileTable(), EntriesFlag );
}
}
exit(0);
}