windows-nt/Source/XPSP1/NT/base/ntsetup/mpk/imagpart/ntfs.c
2020-09-26 16:20:57 +08:00

1268 lines
30 KiB
C

#include "imagpart.h"
#include <malloc.h>
#include <stddef.h>
#include "ntfs.h"
//
// NTFS boot sector.
//
#pragma pack(1)
typedef struct _NTFS_BOOT_SECTOR {
BYTE Jump[3];
BYTE Oem[8];
USHORT BytesPerSector;
BYTE SectorsPerCluster;
USHORT ReservedSectors;
BYTE Fats;
USHORT RootEntries;
USHORT Sectors;
BYTE Media;
USHORT SectorsPerFat;
USHORT SectorsPerTrack;
USHORT Heads;
ULONG HiddenSectors;
ULONG LargeSectors;
BYTE Unused0[4];
ULONG NumberSectors;
ULONG NumberSectorsh;
ULONG MftStartLcn;
ULONG MftStartLcnh;
ULONG Mft2StartLcn;
ULONG Mft2StartLcnh;
CHAR ClustersPerFileRecordSegment;
BYTE Unused1[3];
CHAR DefClusPerIndexAllocBuffer;
BYTE Unused2[3];
ULONG SerialNumberl;
ULONG SerialNumberh;
ULONG Checksum;
BYTE BootStrap[512-86];
USHORT AA55Signature;
} NTFS_BOOT_SECTOR, _far *FPNTFS_BOOT_SECTOR;
#pragma pack()
//
// Globals, which make our life easier.
//
BYTE SectorsPerFrs;
unsigned SegmentsInMft;
FPFILE_RECORD_SEGMENT FrsScratchBuffer;
FPFILE_RECORD_SEGMENT MftLcnFrsScratchBuffer;
typedef struct _MFT_MAPPING {
ULONG Start;
BYTE Count;
} MFT_MAPPING, *PMFT_MAPPING, _far *FPMFT_MAPPING;
FPMFT_MAPPING MftMappings;
FPATTRIBUTE_LIST_ENTRY AttrListBuffer;
#define ATTR_LIST_BUFFER_SIZE 8192
BOOL
pNtfsSetUpMft(
IN HPARTITION PartitionHandle,
IN PARTITION_IMAGE *PartitionImage,
IN FPNTFS_BOOT_SECTOR BootSector
);
BOOL
pNtfsTransferVolumeBitmap(
IN HPARTITION PartitionHandle,
IN PARTITION_IMAGE *PartitionImage,
IN UINT OutputFileHandle
);
BOOL
pNtfsProcessVolumeBitmap(
IN OUT PARTITION_IMAGE *PartitionImage,
IN UINT FileHandle
);
BOOL
pNtfsMultiSectorFixup(
IN OUT FPVOID Buffer
);
FPATTRIBUTE_RECORD
pNtfsLocateAttributeRecord(
IN FPFILE_RECORD_SEGMENT MftFrsBuffer,
IN ULONG TypeCode
);
BOOL
pNtfsReadWholeAttribute(
IN HPARTITION PartitionHandle,
IN PARTITION_IMAGE *PartitionImage,
IN FPATTRIBUTE_RECORD Attribute,
OUT FPVOID Buffer, OPTIONAL
IN UINT OutputFileHandle OPTIONAL
);
BOOL
pNtfsComputeMftLcn(
IN HPARTITION PartitionHandle,
IN ULONG Vcn,
OUT ULONG *Lcn
);
BOOL
pNtfsComputeLcn(
IN ULONG Vcn,
IN FPATTRIBUTE_RECORD Attribute,
OUT ULONG *Lcn,
OUT UINT *RunLength
);
ULONG
pNtfsLcnFromMappingPair(
IN FPBYTE p
);
ULONG
pNtfsVcnFromMappingPair(
IN FPBYTE p
);
BOOL
pNtfsReadFrs(
IN HPARTITION PartitionHandle,
IN PARTITION_IMAGE *PartitionImage,
IN ULONG FileNumber,
OUT FPVOID Buffer
);
BOOL
pNtfsReadMftSectors(
IN HPARTITION PartitionHandle,
IN PARTITION_IMAGE *PartitionImage,
IN ULONG Vbn,
OUT FPBYTE Buffer
);
BOOL
pNtfsSearchFrsForAttrList(
IN HPARTITION PartitionHandle,
IN PARTITION_IMAGE *PartitionImage,
IN FPFILE_RECORD_SEGMENT FrsBuffer,
IN ULONG TypeCode,
OUT ULONG *Frn
);
BOOL
NtfsIsNtfs(
IN HPARTITION PartitionHandle
)
{
FPNTFS_BOOT_SECTOR BootSector;
if(!ReadPartition(PartitionHandle,0,1,IoBuffer)) {
return(FALSE);
}
BootSector = IoBuffer;
//
// Ensure that NTFS appears in the OEM field.
//
if(strncmp(BootSector->Oem,"NTFS ",8)) {
return(FALSE);
}
//
// Check bytes per sector
//
if(BootSector->BytesPerSector != 512) {
return(FALSE);
}
//
// Other checks.
//
if((BootSector->SectorsPerCluster != 1)
&& (BootSector->SectorsPerCluster != 2)
&& (BootSector->SectorsPerCluster != 4)
&& (BootSector->SectorsPerCluster != 8)
&& (BootSector->SectorsPerCluster != 16)
&& (BootSector->SectorsPerCluster != 32)
&& (BootSector->SectorsPerCluster != 64)
&& (BootSector->SectorsPerCluster != 128)) {
return(FALSE);
}
if(BootSector->ReservedSectors
|| BootSector->Fats
|| BootSector->RootEntries
|| BootSector->Sectors
|| BootSector->SectorsPerFat
|| BootSector->LargeSectors) {
return(FALSE);
}
//
// ClustersPerFileRecord can be less than zero if file records
// are smaller than clusters. This number is the negative of a shift count.
// If clusters are smaller than file records then this number is
// still the clusters per file records.
//
if(BootSector->ClustersPerFileRecordSegment <= -9) {
if(BootSector->ClustersPerFileRecordSegment < -31) {
return(FALSE);
}
} else if((BootSector->ClustersPerFileRecordSegment != 1)
&& (BootSector->ClustersPerFileRecordSegment != 2)
&& (BootSector->ClustersPerFileRecordSegment != 4)
&& (BootSector->ClustersPerFileRecordSegment != 8)
&& (BootSector->ClustersPerFileRecordSegment != 16)
&& (BootSector->ClustersPerFileRecordSegment != 32)
&& (BootSector->ClustersPerFileRecordSegment != 64)) {
return(FALSE);
}
//
// ClustersPerIndexAllocationBuffer can be less than zero if index buffers
// are smaller than clusters. This number is the negative of a shift count.
// If clusters are smaller than index buffers then this number is
// still the clusters per index buffers.
//
if(BootSector->DefClusPerIndexAllocBuffer <= -9) {
if(BootSector->DefClusPerIndexAllocBuffer < -31) {
return(FALSE);
}
} else if((BootSector->DefClusPerIndexAllocBuffer != 1)
&& (BootSector->DefClusPerIndexAllocBuffer != 2)
&& (BootSector->DefClusPerIndexAllocBuffer != 4)
&& (BootSector->DefClusPerIndexAllocBuffer != 8)
&& (BootSector->DefClusPerIndexAllocBuffer != 16)
&& (BootSector->DefClusPerIndexAllocBuffer != 32)
&& (BootSector->DefClusPerIndexAllocBuffer != 64)) {
return(FALSE);
}
return(TRUE);
}
BOOL
NtfsInitializeVolumeData(
IN HPARTITION PartitionHandle,
OUT ULONG *TotalSectorCount,
OUT ULONG *NonClusterSectors,
OUT ULONG *ClusterCount,
OUT BYTE *SectorsPerCluster
)
{
FPNTFS_BOOT_SECTOR BootSector;
if(!ReadPartition(PartitionHandle,0,1,IoBuffer)) {
fprintf(stderr,"\n");
fprintf(stderr,textReadFailedAtSector,0L);
fprintf(stderr,"\n");
return(FALSE);
}
BootSector = IoBuffer;
//
// The mirror boot sector is not included in the sector count
// in the bpb.
//
*TotalSectorCount = BootSector->NumberSectors + 1;
*ClusterCount = BootSector->NumberSectors / BootSector->SectorsPerCluster;
*NonClusterSectors = 0;
*SectorsPerCluster = BootSector->SectorsPerCluster;
return(TRUE);
}
BOOL
NtfsBuildClusterBitmap(
IN HPARTITION PartitionHandle,
IN UINT FileHandle,
IN OUT PARTITION_IMAGE *PartitionImage
)
{
FPNTFS_BOOT_SECTOR BootSector;
ULONG u;
//
// Load the boot sector, as we need the bpb fields.
//
BootSector = (FPNTFS_BOOT_SECTOR)((FPBYTE)IoBuffer+(62*512));
if(!ReadPartition(PartitionHandle,0,1,BootSector)) {
fprintf(stderr,"\n");
fprintf(stderr,textReadFailedAtSector,0L);
fprintf(stderr,"\n");
return(FALSE);
}
//
// Validate that we think we can deal with this drive. We impose
// some limitations because of 16 bitness, memory constraints, etc.
//
// We disallow:
//
// Volumes with more than 2^32 sectors
// Volumes with a cluster size > 16K
// Volumes with > 16K per FRS
//
if(BootSector->NumberSectorsh) {
fprintf(stderr,"\n");
fprintf(stderr,textNtfsUnsupportedConfig,0);
fprintf(stderr,"\n");
return(FALSE);
}
if(BootSector->SectorsPerCluster > 32) {
fprintf(stderr,"\n");
fprintf(stderr,textNtfsUnsupportedConfig,1);
fprintf(stderr,"\n");
return(FALSE);
}
if(BootSector->ClustersPerFileRecordSegment < 0) {
u = (1L << (0 - BootSector->ClustersPerFileRecordSegment)) / 512;
} else {
u = BootSector->ClustersPerFileRecordSegment * BootSector->SectorsPerCluster;
}
if(u > 32) {
fprintf(stderr,"\n");
fprintf(stderr,textNtfsUnsupportedConfig,2);
fprintf(stderr,"\n");
return(FALSE);
}
SectorsPerFrs = (BYTE)u;
//
// Initialize mapping information for the MFT
//
if(!pNtfsSetUpMft(PartitionHandle,PartitionImage,BootSector)) {
return(FALSE);
}
//
// Transfer the volume bitmap to the output file.
//
if(!pNtfsTransferVolumeBitmap(PartitionHandle,PartitionImage,FileHandle)) {
return(FALSE);
}
//
// Analyze and fix up the bitmap.
//
if(!pNtfsProcessVolumeBitmap(PartitionImage,FileHandle)) {
return(FALSE);
}
return(TRUE);
}
BOOL
pNtfsSetUpMft(
IN HPARTITION PartitionHandle,
IN PARTITION_IMAGE *PartitionImage,
IN FPNTFS_BOOT_SECTOR BootSector
)
{
ULONG Vbn,Vcn,Lcn,Lbn;
BYTE SectorOffset;
BYTE Remaining;
BYTE Count;
BYTE xCount;
ULONG xStart;
FPATTRIBUTE_RECORD Attribute;
FPATTRIBUTE_LIST_ENTRY AttrListEntry;
unsigned ArraySize;
BOOL b;
printf(textInitNtfsDataStruct);
//
// Allocate a scratch buffer to hold a buffered FRS
//
FrsScratchBuffer = malloc(SectorsPerFrs * 512);
if(!FrsScratchBuffer) {
fprintf(stderr,"\n%s\n",textOOM);
return(FALSE);
}
//
// Allocate a scratch buffer for lookups
//
MftLcnFrsScratchBuffer = malloc(SectorsPerFrs * 512);
if(!MftLcnFrsScratchBuffer) {
fprintf(stderr,"\n%s\n",textOOM);
return(FALSE);
}
//
// Allocate an initial buffer to store mappings.
//
MftMappings = malloc(sizeof(MFT_MAPPING));
if(!MftMappings) {
fprintf(stderr,"\n%s\n",textOOM);
return(FALSE);
}
ArraySize = 1;
//
// Allocate an attribute list buffer.
//
AttrListBuffer = malloc(ATTR_LIST_BUFFER_SIZE);
if(!AttrListBuffer) {
fprintf(stderr,"\n%s\n",textOOM);
return(FALSE);
}
//
// Read FRS 0 into the first MFT FRS buffer, being sure
// to resolve the Update Sequence Array. Remember the physical
// location in the Lbn array.
//
MftMappings[0].Start = BootSector->MftStartLcn * BootSector->SectorsPerCluster;
MftMappings[0].Count = SectorsPerFrs;
SegmentsInMft = 1;
if(!ReadPartition(PartitionHandle,MftMappings[0].Start,MftMappings[0].Count,(FPBYTE)IoBuffer+(61*512))) {
fprintf(stderr,"\n");
fprintf(stderr,textReadFailedAtSector,MftMappings[0].Start);
fprintf(stderr,"\n");
return(FALSE);
}
memmove(FrsScratchBuffer,(FPBYTE)IoBuffer+(61*512),MftMappings[0].Count*512);
printf(".");
if(!pNtfsMultiSectorFixup(FrsScratchBuffer)) {
return(FALSE);
}
printf(".");
//
// Determine whether the MFT has an Attribute List attribute,
// and if so, read it.
//
if(Attribute = pNtfsLocateAttributeRecord(FrsScratchBuffer,$ATTRIBUTE_LIST)) {
b = pNtfsReadWholeAttribute(
PartitionHandle,
PartitionImage,
Attribute,
AttrListBuffer,
0
);
if(!b) {
return(FALSE);
}
printf(".");
AttrListEntry = AttrListBuffer;
//
// Traverse the attribute list looking for the first
// entry for the $DATA type. We know it must have at least one.
// Note that we then immediately skip this one because we already
// handled the 0th FRS.
//
while(AttrListEntry->TypeCode != $DATA) {
AttrListEntry = (FPATTRIBUTE_LIST_ENTRY)((FPBYTE)AttrListEntry + AttrListEntry->Length);
}
do {
AttrListEntry = (FPATTRIBUTE_LIST_ENTRY)((FPBYTE)AttrListEntry + AttrListEntry->Length);
if(AttrListEntry->TypeCode == $DATA) {
//
// Find the start sector and sector count for the runs for this
// file record (max 2 runs). The mapping for this must be
// in a file record already visited. Find the Vcn and cluster
// offset for this FRS. Use LookupMftLcn to find the Lcn.
//
// Convert from Frs to sectors, then to Vcn.
//
Vbn = AttrListEntry->SegmentReference.LowPart * SectorsPerFrs;
Vcn = Vbn / BootSector->SectorsPerCluster;
SectorOffset = (BYTE)(Vbn % BootSector->SectorsPerCluster);
if(!pNtfsComputeMftLcn(PartitionHandle,Vcn,&Lcn)) {
return(FALSE);
}
printf(".");
//
// Sectors remaining in this frs is initially the whole frs
//
Remaining = SectorsPerFrs;
//
// Change the LCN back into an LBN and add the remainder
// back in to get the sector we want to read. Then store
// this in the next Lcn array slot.
//
Lbn = (Lcn * BootSector->SectorsPerCluster) + SectorOffset;
xStart = Lbn;
xCount = BootSector->SectorsPerCluster - SectorOffset;
if(xCount > Remaining) {
xCount = Remaining;
}
Count = xCount;
while(Remaining -= Count) {
Vbn += Count;
Vcn = Vbn / BootSector->SectorsPerCluster;
if(!pNtfsComputeMftLcn(PartitionHandle,Vcn,&Lcn)) {
return(FALSE);
}
Lbn = Lcn * BootSector->SectorsPerCluster;
Count = BootSector->SectorsPerCluster;
if(Count > Remaining) {
Count = Remaining;
}
//
// Check contiguity
//
if((xStart + xCount) == Lbn) {
xCount += Count;
} else {
MftMappings = realloc(MftMappings,(ArraySize+1)*sizeof(MFT_MAPPING));
if(!MftMappings) {
fprintf(stderr,"\n%s\n",textOOM);
return(FALSE);
}
MftMappings[ArraySize].Start = xStart;
MftMappings[ArraySize].Count = xCount;
ArraySize++;
xStart = Lbn;
xCount = Count;
}
printf(".");
}
MftMappings = realloc(MftMappings,(ArraySize+1)*sizeof(MFT_MAPPING));
if(!MftMappings) {
fprintf(stderr,"\n%s\n",textOOM);
return(FALSE);
}
MftMappings[ArraySize].Start = xStart;
MftMappings[ArraySize].Count = xCount;
ArraySize++;
SegmentsInMft++;
}
} while(AttrListEntry->TypeCode == $DATA);
} else {
printf(".");
}
return(TRUE);
}
BOOL
pNtfsTransferVolumeBitmap(
IN HPARTITION PartitionHandle,
IN PARTITION_IMAGE *PartitionImage,
IN UINT OutputFileHandle
)
{
FPFILE_RECORD_SEGMENT BitmapFrsBuffer;
FPATTRIBUTE_RECORD Record;
BOOL b;
ULONG Frn;
//
// Allocate a scratch buffer for the volume bitmap frs,
// then locate the frs for the volume bitmap and read it in.
//
BitmapFrsBuffer = malloc(SectorsPerFrs * 512);
if(!BitmapFrsBuffer) {
fprintf(stderr,"\n%s\n",textOOM);
return(FALSE);
}
if(!pNtfsReadFrs(PartitionHandle,PartitionImage,BIT_MAP_FILE_NUMBER,BitmapFrsBuffer)) {
free(BitmapFrsBuffer);
return(FALSE);
}
printf(".");
//
// Attempt to locate a $DATA attribute for the volume bitmap.
// If there is none, get the attribute list.
//
Record = pNtfsLocateAttributeRecord(BitmapFrsBuffer,$DATA);
printf(".");
if(!Record) {
b = pNtfsSearchFrsForAttrList(
PartitionHandle,
PartitionImage,
BitmapFrsBuffer,
$DATA,
&Frn
);
if(!b) {
free(BitmapFrsBuffer);
return(FALSE);
}
b = pNtfsReadFrs(PartitionHandle,PartitionImage,Frn,BitmapFrsBuffer);
if(!b) {
free(BitmapFrsBuffer);
return(FALSE);
}
printf(".");
Record = pNtfsLocateAttributeRecord(BitmapFrsBuffer,$DATA);
if(!Record) {
fprintf(stderr,"\n");
fprintf(stderr,textNtfsCorrupt,0);
fprintf(stderr,"\n");
free(BitmapFrsBuffer);
return(FALSE);
}
}
printf(".\n");
b = pNtfsReadWholeAttribute(
PartitionHandle,
PartitionImage,
Record,
NULL,
OutputFileHandle
);
free(BitmapFrsBuffer);
return(b);
}
BOOL
pNtfsProcessVolumeBitmap(
IN OUT PARTITION_IMAGE *PartitionImage,
IN UINT FileHandle
)
{
ULONG LastUsed;
ULONG TotalUsed;
ULONG BaseCluster;
UINT Offset;
UINT Read;
ULONG i;
extern BYTE BitValue[8];
//
// Rewind the file
//
DosSeek(FileHandle,0,DOSSEEK_START);
LastUsed = 0;
TotalUsed = 0;
BaseCluster = 0;
for(i=0; i<PartitionImage->ClusterCount; i++,Offset++) {
//
// Reload bitmap buffer if necessary
//
if(!(i % (512*8))) {
if(_dos_read(FileHandle,IoBuffer,512,&Read) || (Read != 512)) {
fprintf(stderr,"\n%s\n",textFileReadFailed);
return(FALSE);
}
printf(textProcessingNtfsBitmap,100*i/PartitionImage->ClusterCount);
printf("\r");
Offset = 0;
if(i) {
BaseCluster += (512*8);
}
}
if(((FPBYTE)IoBuffer)[Offset/8] & BitValue[Offset%8]) {
TotalUsed++;
LastUsed = i;
}
}
PartitionImage->UsedClusterCount = TotalUsed;
PartitionImage->LastUsedCluster = LastUsed;
//
// Calculate the number of sectors in the bitmap.
// It's impossible for an NTFS drive to have none used since
// all fs data structures are part of clusters, so we don't worry
// about boundary cases.
//
i = (LastUsed / (8*512)) + 1;
//
// Truncate the output file.
//
if((DosSeek(FileHandle,i*512,DOSSEEK_START) != i*512)
|| _dos_write(FileHandle,IoBuffer,0,&Read)) {
fprintf(stderr,"\n%s\n",textFileWriteError);
return(FALSE);
}
printf(textProcessingNtfsBitmap,100);
printf("\n");
return(TRUE);
}
BOOL
pNtfsMultiSectorFixup(
IN OUT FPVOID Buffer
)
{
UINT Size,Offset;
FPUINT p,q;
Offset = ((FPMULTI_SECTOR_HEADER)Buffer)->UpdateArrayOfs;
Size = ((FPMULTI_SECTOR_HEADER)Buffer)->UpdateArraySize;
if(!Size) {
fprintf(stderr,"\n");
fprintf(stderr,textNtfsCorrupt,1);
fprintf(stderr,"\n");
return(FALSE);
}
p = (FPUINT)((FPBYTE)Buffer + Offset) + 1;
q = (FPUINT)((FPBYTE)Buffer + SEQUENCE_NUMBER_STRIDE) - 1;
while(--Size) {
*q = *p++;
q = (FPUINT)((FPBYTE)q + SEQUENCE_NUMBER_STRIDE);
}
return(TRUE);
}
FPATTRIBUTE_RECORD
pNtfsLocateAttributeRecord(
IN FPFILE_RECORD_SEGMENT FrsBuffer,
IN ULONG TypeCode
)
{
FPATTRIBUTE_RECORD Attr;
Attr = (FPATTRIBUTE_RECORD)((FPBYTE)FrsBuffer + FrsBuffer->FirstAttribute);
while(Attr->TypeCode != $END) {
if((Attr->TypeCode == TypeCode) && !Attr->NameLength) {
return(Attr);
}
Attr = (FPATTRIBUTE_RECORD)((FPBYTE)Attr + Attr->RecordLength);
}
return(NULL);
}
BOOL
pNtfsReadWholeAttribute(
IN HPARTITION PartitionHandle,
IN PARTITION_IMAGE *PartitionImage,
IN FPATTRIBUTE_RECORD Attribute,
OUT FPVOID Buffer, OPTIONAL
IN UINT OutputFileHandle OPTIONAL
)
{
FPRESIDENT_ATTRIBUTE_FORM Resident;
FPNONRESIDENT_ATTRIBUTE_FORM NonResident;
ULONG ClusterCount;
UINT RunLength;
ULONG Vcn,Lcn,Lbn;
UINT Sectors;
BYTE x;
UINT Written;
ULONG SectorsDone;
ULONG OriginalCount;
if(Attribute->FormCode == RESIDENT_FORM) {
Resident = (FPRESIDENT_ATTRIBUTE_FORM)((FPBYTE)Attribute + offsetof(ATTRIBUTE_RECORD,FormUnion));
if(Buffer) {
if(Resident->ValueLength > ATTR_LIST_BUFFER_SIZE) {
fprintf(stderr,"\n");
fprintf(stderr,textNtfsUnsupportedConfig,3);
fprintf(stderr,"\n");
return(FALSE);
}
memmove(
Buffer,
(FPBYTE)Attribute + Resident->ValueOffset,
(UINT)Resident->ValueLength
);
return(TRUE);
} else {
fprintf(stderr,"\n");
fprintf(stderr,textNtfsUnsupportedConfig,4);
fprintf(stderr,"\n");
return(FALSE);
}
}
NonResident = (FPNONRESIDENT_ATTRIBUTE_FORM)((FPBYTE)Attribute + offsetof(ATTRIBUTE_RECORD,FormUnion));
ClusterCount = NonResident->HighestVcn + 1;
if(Buffer && ((ClusterCount * PartitionImage->SectorsPerCluster * 512) > (ULONG)ATTR_LIST_BUFFER_SIZE)) {
fprintf(stderr,"\n");
fprintf(stderr,textNtfsUnsupportedConfig,5);
fprintf(stderr,"\n");
return(FALSE);
}
Vcn = 0;
if(!Buffer) {
OriginalCount = ClusterCount * PartitionImage->SectorsPerCluster;
SectorsDone = 0;
printf(textNtfsBuildingBitmap,0);
printf("\r");
}
while(ClusterCount) {
pNtfsComputeLcn(Vcn,Attribute,&Lcn,&RunLength);
if(ClusterCount < (ULONG)RunLength) {
RunLength = (UINT)ClusterCount;
}
Sectors = RunLength * PartitionImage->SectorsPerCluster;
Lbn = Lcn * PartitionImage->SectorsPerCluster;
while(Sectors) {
x = (BYTE)((Sectors > 32) ? 32 : Sectors);
if(!ReadPartition(PartitionHandle,Lbn,x,IoBuffer)) {
fprintf(stderr,"\n");
fprintf(stderr,textReadFailedAtSector,Lbn);
fprintf(stderr,"\n");
return(FALSE);
}
if(Buffer) {
memmove(Buffer,IoBuffer,x*512);
(FPBYTE)Buffer += x*512;
} else {
if(_dos_write(OutputFileHandle,IoBuffer,x*512,&Written) || (Written != x*512U)) {
fprintf(stderr,"\n%s\n",textFileWriteError);
return(FALSE);
}
}
Sectors -= x;
Lbn += x;
if(!Buffer) {
SectorsDone += x;
printf(textNtfsBuildingBitmap,100 * SectorsDone / OriginalCount);
printf("\r");
}
}
Vcn += RunLength;
ClusterCount -= RunLength;
}
if(!Buffer) {
printf("\n");
}
return(TRUE);
}
BOOL
pNtfsComputeMftLcn(
IN HPARTITION PartitionHandle,
IN ULONG Vcn,
OUT ULONG *Lcn
)
{
UINT Segment;
FPMFT_MAPPING MftMapping;
BYTE Remaining;
FPBYTE Buffer;
FPATTRIBUTE_RECORD Attribute;
UINT RunLength;
MftMapping = MftMappings;
for(Segment=0; Segment<SegmentsInMft; Segment++) {
Remaining = SectorsPerFrs;
Buffer = (FPBYTE)MftLcnFrsScratchBuffer;
while(Remaining) {
if(!ReadPartition(PartitionHandle,MftMapping->Start,MftMapping->Count,IoBuffer)) {
fprintf(stderr,"\n");
fprintf(stderr,textReadFailedAtSector,MftMapping->Start);
fprintf(stderr,"\n");
return(FALSE);
}
memmove(Buffer,IoBuffer,MftMapping->Count*512);
Remaining -= MftMapping->Count;
Buffer += MftMapping->Count*512;
MftMapping++;
}
if(!pNtfsMultiSectorFixup(MftLcnFrsScratchBuffer)) {
return(FALSE);
}
Attribute = pNtfsLocateAttributeRecord(MftLcnFrsScratchBuffer,$DATA);
if(!Attribute) {
fprintf(stderr,"\n");
fprintf(stderr,textNtfsCorrupt,2);
fprintf(stderr,"\n");
return(FALSE);
}
if(pNtfsComputeLcn(Vcn,Attribute,Lcn,&RunLength)) {
return(TRUE);
}
}
//
// Didn't find it, something is wrong
//
fprintf(stderr,"\n");
fprintf(stderr,textNtfsCorrupt,3);
fprintf(stderr,"\n");
return(FALSE);
}
BOOL
pNtfsComputeLcn(
IN ULONG Vcn,
IN FPATTRIBUTE_RECORD Attribute,
OUT ULONG *Lcn,
OUT UINT *RunLength
)
{
FPNONRESIDENT_ATTRIBUTE_FORM NonResident;
FPBYTE p;
ULONG CurrentLcn,CurrentVcn,NextVcn;
BYTE x1,x2;
//
// Make sure we're non-resident
//
if(Attribute->FormCode == RESIDENT_FORM) {
return(FALSE);
}
//
// Address the nonresident info
//
NonResident = (FPNONRESIDENT_ATTRIBUTE_FORM)((FPBYTE)Attribute + offsetof(ATTRIBUTE_RECORD,FormUnion));
//
// See if the desired VCN is in range
//
if((Vcn > NonResident->HighestVcn) || (Vcn < NonResident->LowestVcn)) {
return(FALSE);
}
p = (FPBYTE)Attribute + NonResident->MappingPairOffset;
CurrentLcn = 0;
CurrentVcn = NonResident->LowestVcn;
//
// The loop condition checks the count byte
//
while(*p) {
CurrentLcn += pNtfsLcnFromMappingPair(p);
NextVcn = CurrentVcn + pNtfsVcnFromMappingPair(p);
if(Vcn < NextVcn) {
//
// Found it.
//
*RunLength = (UINT)(NextVcn - Vcn);
*Lcn = (Vcn - CurrentVcn) + CurrentLcn;
return(TRUE);
}
CurrentVcn = NextVcn;
x1 = *p;
x2 = x1 & (BYTE)0xf;
x1 >>= 4;
p += x1+x2+1;
}
return(FALSE);
}
ULONG
pNtfsLcnFromMappingPair(
IN FPBYTE p
)
{
BYTE v,l;
long x;
v = *p & (BYTE)0xf;
l = (BYTE)(*p >> 4);
if(!l) {
return(0);
}
p += v + l;
x = *(FPCHAR)p;
l--;
p--;
while(l) {
x <<= 8;
x |= *p;
p--;
l--;
}
return(x);
}
ULONG
pNtfsVcnFromMappingPair(
IN FPBYTE p
)
{
BYTE v;
long x;
v = *p & (BYTE)0xf;
if(!v) {
return(0);
}
p += v;
x = *(FPCHAR)p;
v--;
p--;
while(v) {
x <<= 8;
x |= *p;
p--;
v--;
}
return(x);
}
BOOL
pNtfsReadFrs(
IN HPARTITION PartitionHandle,
IN PARTITION_IMAGE *PartitionImage,
IN ULONG FileNumber,
OUT FPVOID Buffer
)
{
if(!pNtfsReadMftSectors(PartitionHandle,PartitionImage,FileNumber*SectorsPerFrs,Buffer)) {
return(FALSE);
}
pNtfsMultiSectorFixup(Buffer);
return(TRUE);
}
BOOL
pNtfsReadMftSectors(
IN HPARTITION PartitionHandle,
IN PARTITION_IMAGE *PartitionImage,
IN ULONG Vbn,
OUT FPBYTE Buffer
)
{
BYTE Remaining;
BYTE Count;
ULONG Vcn;
BYTE r;
ULONG Lcn;
ULONG Lbn;
ULONG NextVbn;
Remaining = SectorsPerFrs;
while(Remaining) {
//
// Get VCN
//
Vcn = Vbn / PartitionImage->SectorsPerCluster;
r = (BYTE)(Vbn % PartitionImage->SectorsPerCluster);
if(!pNtfsComputeMftLcn(PartitionHandle,Vcn,&Lcn)) {
return(FALSE);
}
Lbn = (Lcn * PartitionImage->SectorsPerCluster) + r;
//
// Read in only a single cluster at a time to avoid problems
// with fragmented runs in the mft.
//
if(Remaining > PartitionImage->SectorsPerCluster) {
Count = PartitionImage->SectorsPerCluster;
Remaining -= PartitionImage->SectorsPerCluster;
NextVbn = Vbn + PartitionImage->SectorsPerCluster;
} else {
//
// No need to worry about NextVbn since we'll break out of
// the loop (Remaining will be 0).
//
Count = Remaining;
Remaining = 0;
}
if(!ReadPartition(PartitionHandle,Lbn,Count,IoBuffer)) {
fprintf(stderr,"\n");
fprintf(stderr,textReadFailedAtSector,Lbn);
fprintf(stderr,"\n");
return(FALSE);
}
memmove(Buffer,IoBuffer,Count*512);
Buffer += Count*512;
Vbn = NextVbn;
}
return(TRUE);
}
BOOL
pNtfsSearchFrsForAttrList(
IN HPARTITION PartitionHandle,
IN PARTITION_IMAGE *PartitionImage,
IN FPFILE_RECORD_SEGMENT FrsBuffer,
IN ULONG TypeCode,
OUT ULONG *Frn
)
{
FPATTRIBUTE_RECORD Record;
FPATTRIBUTE_LIST_ENTRY Attribute;
BOOL b;
Record = pNtfsLocateAttributeRecord(FrsBuffer,$ATTRIBUTE_LIST);
if(!Record) {
fprintf(stderr,"\n");
fprintf(stderr,textNtfsCorrupt,4);
fprintf(stderr,"\n");
return(FALSE);
}
//
// Got it, now read the attribute list into the attribute list
// scratch buffer
//
b = pNtfsReadWholeAttribute(
PartitionHandle,
PartitionImage,
Record,
AttrListBuffer,
0
);
if(!b) {
return(FALSE);
}
Attribute = AttrListBuffer;
nextrec:
if(Attribute->TypeCode == TypeCode) {
*Frn = Attribute->SegmentReference.LowPart;
return(TRUE);
}
if(Attribute->TypeCode == $END) {
fprintf(stderr,"\n");
fprintf(stderr,textNtfsCorrupt,5);
fprintf(stderr,"\n");
return(FALSE);
}
if(!Attribute->Length) {
fprintf(stderr,"\n");
fprintf(stderr,textNtfsCorrupt,6);
fprintf(stderr,"\n");
return(FALSE);
}
Attribute = (FPATTRIBUTE_LIST_ENTRY)((FPBYTE)Attribute + Attribute->Length);
goto nextrec;
}