#include "imagpart.h" #include #include #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; iClusterCount; 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; SegmentStart,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; }