Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions README
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
libdmg filesystem support
==========================

libdmg now ships with a small filesystem descriptor API that separates
UDIF/DMG generation from the filesystem-specific metadata gathering. The
new `DMGVolumeInfo` structure plus `buildDmgWithVolumeInfo()` entry point
lets external libraries (for instance, an upcoming APFS helper) provide
the block counts, signatures, and partition labels without having to
modify the DMG writer again.

Extraction helpers were also relaxed to look for both `Apple_HFS*` and
`Apple_APFS` partition tags so APFS volumes can be located inside hybrid
images even before the dedicated filesystem library lands.
15 changes: 14 additions & 1 deletion include/libdmg-1.0/dmg.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#define ATTRIBUTE_HDIUTIL 0x0050

#define HFSX_VOLUME_TYPE "Apple_HFSX"
#define APFS_VOLUME_TYPE "Apple_APFS"

#define DDM_SIZE 0x1
#define PARTITION_SIZE 0x3f
Expand All @@ -44,6 +45,17 @@
#define BOOTCODE_DMMY 0x444D4D59
#define BOOTCODE_GOON 0x676F6F6E

typedef struct {
const char* partition_name;
const char* partition_type;
uint32_t blockSize;
uint64_t totalBlocks;
uint64_t freeBlocks;
uint32_t modifyDate;
uint16_t signature;
char isHFS;
} DMGVolumeInfo;

enum {
kUDIFFlagsFlattened = 1
};
Expand Down Expand Up @@ -314,7 +326,7 @@ extern "C" {
ResourceData* getDataByID(ResourceKey* resource, int id);
ResourceKey* insertData(ResourceKey* resources, const char* key, int id, const char* name, const char* data, size_t dataLength, uint32_t attributes);
ResourceKey* makePlst();
ResourceKey* makeSize(HFSPlusVolumeHeader* volumeHeader);
ResourceKey* makeSize(const DMGVolumeInfo* volumeInfo);

void flipDriverDescriptorRecord(DriverDescriptorRecord* record, char out);
void flipPartition(Partition* partition, char out, unsigned int BlockSize);
Expand All @@ -337,6 +349,7 @@ extern "C" {

int extractDmg(AbstractFile* abstractIn, AbstractFile* abstractOut, int partNum);
int buildDmg(AbstractFile* abstractIn, AbstractFile* abstractOut, unsigned int BlockSize);
int buildDmgWithVolumeInfo(AbstractFile* abstractIn, AbstractFile* abstractOut, unsigned int BlockSize, const DMGVolumeInfo* volumeInfo);
int convertToISO(AbstractFile* abstractIn, AbstractFile* abstractOut);
int convertToDMG(AbstractFile* abstractIn, AbstractFile* abstractOut);
#ifdef __cplusplus
Expand Down
1 change: 1 addition & 0 deletions include/libdmg-1.0/dmglib.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ extern "C" {
#endif
int extractDmg(AbstractFile* abstractIn, AbstractFile* abstractOut, int partNum);
int buildDmg(AbstractFile* abstractIn, AbstractFile* abstractOut, unsigned int BlockSize);
int buildDmgWithVolumeInfo(AbstractFile* abstractIn, AbstractFile* abstractOut, unsigned int BlockSize, const DMGVolumeInfo* volumeInfo);

int convertToDMG(AbstractFile* abstractIn, AbstractFile* abstractOut);
int convertToISO(AbstractFile* abstractIn, AbstractFile* abstractOut);
Expand Down
4 changes: 3 additions & 1 deletion src/dmgfile.c
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,9 @@ io_func* openDmgFilePartition(AbstractFile* abstractIn, int partition) {
((DMG*)toReturn->data)->offset = partitions[partition].pmPyPartStart * BlockSize;
} else {
for(i = 0; i < numPartitions; i++) {
if(strcmp((char*)partitions->pmParType, "Apple_HFSX") == 0 || strcmp((char*)partitions->pmParType, "Apple_HFS") == 0) {
if(strcmp((char*)partitions->pmParType, HFSX_VOLUME_TYPE) == 0 ||
strcmp((char*)partitions->pmParType, "Apple_HFS") == 0 ||
strcmp((char*)partitions->pmParType, APFS_VOLUME_TYPE) == 0) {
((DMG*)toReturn->data)->offset = partitions->pmPyPartStart * BlockSize;
break;
}
Expand Down
167 changes: 100 additions & 67 deletions src/dmglib.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ int extractDmg(AbstractFile* abstractIn, AbstractFile* abstractOut, int partNum)
if(partNum < 0) {
blkxData = getResourceByKey(resources, "blkx")->data;
while(blkxData != NULL) {
if(strstr(blkxData->name, "Apple_HFS") != NULL) {
if(strstr(blkxData->name, "Apple_HFS") != NULL ||
strstr(blkxData->name, APFS_VOLUME_TYPE) != NULL) {
break;
}
blkxData = blkxData->next;
Expand Down Expand Up @@ -85,94 +86,95 @@ uint32_t calculateMasterChecksum(ResourceKey* resources) {
return result;
}

int buildDmg(AbstractFile* abstractIn, AbstractFile* abstractOut, unsigned int BlockSize) {
io_func* io;
Volume* volume;

HFSPlusVolumeHeader* volumeHeader;
int buildDmgWithVolumeInfo(AbstractFile* abstractIn, AbstractFile* abstractOut, unsigned int BlockSize, const DMGVolumeInfo* volumeInfo) {
DriverDescriptorRecord* DDM;
Partition* partitions;

ResourceKey* resources;
ResourceKey* curResource;

NSizResource* nsiz;
NSizResource* myNSiz;
CSumResource csum;

BLKXTable* blkx;
ChecksumToken uncompressedToken;

ChecksumToken dataForkToken;

UDIFResourceFile koly;

off_t plistOffset;
uint32_t plistSize;
uint32_t dataForkChecksum;

io = IOFuncFromAbstractFile(abstractIn);
volume = openVolume(io);
volumeHeader = volume->volumeHeader;

const char* partitionType;
const char* partitionName;
uint64_t volumeSectors;

if(volumeHeader->signature != HFSX_SIGNATURE) {
printf("Warning: ASR data only reverse engineered for case-sensitive HFS+ volumes\n");fflush(stdout);
if(volumeInfo == NULL) {
fprintf(stderr, "buildDmgWithVolumeInfo: volume information is required\n");
return FALSE;
}


if(volumeInfo->blockSize == 0) {
fprintf(stderr, "buildDmgWithVolumeInfo: invalid filesystem block size\n");
return FALSE;
}

if(volumeInfo->totalBlocks == 0) {
fprintf(stderr, "buildDmgWithVolumeInfo: invalid total block count\n");
return FALSE;
}

partitionType = (volumeInfo->partition_type != NULL) ? volumeInfo->partition_type : HFSX_VOLUME_TYPE;
partitionName = (volumeInfo->partition_name != NULL) ? volumeInfo->partition_name : "Mac_OS_X";
volumeSectors = (volumeInfo->totalBlocks * (uint64_t) volumeInfo->blockSize) / SECTOR_SIZE;

resources = NULL;
nsiz = NULL;

memset(&dataForkToken, 0, sizeof(ChecksumToken));

printf("Creating and writing DDM and partition map...\n"); fflush(stdout);

DDM = createDriverDescriptorMap((volumeHeader->totalBlocks * volumeHeader->blockSize)/SECTOR_SIZE, BlockSize);

partitions = createApplePartitionMap((volumeHeader->totalBlocks * volumeHeader->blockSize)/SECTOR_SIZE, HFSX_VOLUME_TYPE, BlockSize);


DDM = createDriverDescriptorMap((uint32_t) volumeSectors, BlockSize);
partitions = createApplePartitionMap((uint32_t) volumeSectors, partitionType, BlockSize);

int pNum = writeDriverDescriptorMap(-1, abstractOut, DDM, BlockSize, &CRCProxy, (void*) (&dataForkToken), &resources);
free(DDM);
pNum = writeApplePartitionMap(pNum, abstractOut, partitions, BlockSize, &CRCProxy, (void*) (&dataForkToken), &resources, &nsiz);
free(partitions);
pNum = writeATAPI(pNum, abstractOut, BlockSize, &CRCProxy, (void*) (&dataForkToken), &resources, &nsiz);

memset(&uncompressedToken, 0, sizeof(uncompressedToken));
SHA1Init(&(uncompressedToken.sha1));

printf("Writing main data blkx...\n"); fflush(stdout);

abstractIn->seek(abstractIn, 0);
blkx = insertBLKX(abstractOut, abstractIn, USER_OFFSET, (volumeHeader->totalBlocks * volumeHeader->blockSize)/SECTOR_SIZE,
pNum, CHECKSUM_CRC32, &BlockSHA1CRC, &uncompressedToken, &CRCProxy, &dataForkToken, volume, 1);
blkx = insertBLKX(abstractOut, abstractIn, USER_OFFSET, (uint32_t) volumeSectors,
pNum, CHECKSUM_CRC32, &BlockSHA1CRC, &uncompressedToken, &CRCProxy, &dataForkToken, NULL, 1);

blkx->checksum.data[0] = uncompressedToken.crc;
printf("Inserting main blkx...\n"); fflush(stdout);

char pName[100];
sprintf(pName, "Mac_OS_X (Apple_HFSX : %d)", pNum + 1);
sprintf(pName, "%s (%s : %d)", partitionName, partitionType, pNum + 1);
resources = insertData(resources, "blkx", pNum, pName, (const char*) blkx, sizeof(BLKXTable) + (blkx->blocksRunCount * sizeof(BLKXRun)), ATTRIBUTE_HDIUTIL);
free(blkx);

printf("Inserting cSum data...\n"); fflush(stdout);

csum.version = 1;
csum.type = CHECKSUM_MKBLOCK;
csum.checksum = uncompressedToken.block;

resources = insertData(resources, "cSum", 2, "", (const char*) (&csum), sizeof(csum), 0);

printf("Inserting nsiz data\n"); fflush(stdout);

myNSiz = (NSizResource*) malloc(sizeof(NSizResource));
memset(myNSiz, 0, sizeof(NSizResource));
myNSiz->isVolume = TRUE;
myNSiz->blockChecksum2 = uncompressedToken.block;
myNSiz->partitionNumber = 2;
myNSiz->version = 6;
myNSiz->bytes = (volumeHeader->totalBlocks - volumeHeader->freeBlocks) * volumeHeader->blockSize;
myNSiz->modifyDate = volumeHeader->modifyDate;
myNSiz->volumeSignature = volumeHeader->signature;
myNSiz->bytes = (volumeInfo->totalBlocks - volumeInfo->freeBlocks) * volumeInfo->blockSize;
myNSiz->modifyDate = volumeInfo->modifyDate;
myNSiz->volumeSignature = volumeInfo->signature;
myNSiz->sha1Digest = (unsigned char *)malloc(20);
SHA1Final(myNSiz->sha1Digest, &(uncompressedToken.sha1));
myNSiz->next = NULL;
Expand All @@ -186,16 +188,16 @@ int buildDmg(AbstractFile* abstractIn, AbstractFile* abstractOut, unsigned int B
}
curNsiz->next = myNSiz;
}

pNum++;

printf("Writing free partition...\n"); fflush(stdout);
pNum = writeFreePartition(pNum, abstractOut, USER_OFFSET + (volumeHeader->totalBlocks * volumeHeader->blockSize)/SECTOR_SIZE,

pNum = writeFreePartition(pNum, abstractOut, USER_OFFSET + (uint32_t) volumeSectors,
(FREE_SIZE + (BlockSize / SECTOR_SIZE / 2)) / (BlockSize / SECTOR_SIZE) * (BlockSize / SECTOR_SIZE), &resources);

dataForkChecksum = dataForkToken.crc;

printf("Writing XML data...\n"); fflush(stdout);
curResource = resources;
while(curResource->next != NULL)
Expand All @@ -204,19 +206,19 @@ int buildDmg(AbstractFile* abstractIn, AbstractFile* abstractOut, unsigned int B
curResource->next = writeNSiz(nsiz);
curResource = curResource->next;
releaseNSiz(nsiz);

curResource->next = makePlst();
curResource = curResource->next;
curResource->next = makeSize(volumeHeader);

curResource->next = makeSize(volumeInfo);
curResource = curResource->next;

plistOffset = abstractOut->tell(abstractOut);
writeResources(abstractOut, resources);
plistSize = abstractOut->tell(abstractOut) - plistOffset;

printf("Generating UDIF metadata...\n"); fflush(stdout);

koly.fUDIFSignature = KOLY_SIGNATURE;
koly.fUDIFVersion = 4;
koly.fUDIFHeaderSize = sizeof(koly);
Expand All @@ -226,7 +228,7 @@ int buildDmg(AbstractFile* abstractIn, AbstractFile* abstractOut, unsigned int B
koly.fUDIFDataForkLength = plistOffset;
koly.fUDIFRsrcForkOffset = 0;
koly.fUDIFRsrcForkLength = 0;

koly.fUDIFSegmentNumber = 1;
koly.fUDIFSegmentCount = 1;
koly.fUDIFSegmentID.data1 = rand();
Expand All @@ -240,37 +242,68 @@ int buildDmg(AbstractFile* abstractIn, AbstractFile* abstractOut, unsigned int B
koly.fUDIFXMLOffset = plistOffset;
koly.fUDIFXMLLength = plistSize;
memset(&(koly.reserved1), 0, 0x78);

koly.fUDIFMasterChecksum.type = CHECKSUM_CRC32;
koly.fUDIFMasterChecksum.size = 0x20;
memset(&koly.fUDIFMasterChecksum.data, 0, sizeof(koly.fUDIFMasterChecksum.data));
koly.fUDIFMasterChecksum.data[0] = calculateMasterChecksum(resources);
printf("Master checksum: %x\n", koly.fUDIFMasterChecksum.data[0]); fflush(stdout);
printf("Master checksum: %x\n", koly.fUDIFMasterChecksum.data[0]); fflush(stdout);

koly.fUDIFImageVariant = kUDIFDeviceImageType;
koly.fUDIFSectorCount = (volumeHeader->totalBlocks * volumeHeader->blockSize)/SECTOR_SIZE
koly.fUDIFSectorCount = volumeSectors
+ ((EXTRA_SIZE + (BlockSize / SECTOR_SIZE / 2)) / (BlockSize / SECTOR_SIZE) * (BlockSize / SECTOR_SIZE));
koly.reserved2 = 0;
koly.reserved3 = 0;
koly.reserved4 = 0;
printf("Writing out UDIF resource file...\n"); fflush(stdout);

printf("Writing out UDIF resource file...\n"); fflush(stdout);

writeUDIFResourceFile(abstractOut, &koly);

printf("Cleaning up...\n"); fflush(stdout);

releaseResources(resources);

abstractOut->close(abstractOut);
closeVolume(volume);
CLOSE(io);


printf("Done.\n"); fflush(stdout);

return TRUE;
}

int buildDmg(AbstractFile* abstractIn, AbstractFile* abstractOut, unsigned int BlockSize) {
io_func* io;
Volume* volume;
HFSPlusVolumeHeader* volumeHeader;
DMGVolumeInfo volumeInfo;
int result;

io = IOFuncFromAbstractFile(abstractIn);
volume = openVolume(io);
volumeHeader = volume->volumeHeader;

if(volumeHeader->signature != HFSX_SIGNATURE) {
printf("Warning: ASR data only reverse engineered for case-sensitive HFS+ volumes\n");fflush(stdout);
}

memset(&volumeInfo, 0, sizeof(volumeInfo));
volumeInfo.partition_name = "Mac_OS_X";
volumeInfo.partition_type = HFSX_VOLUME_TYPE;
volumeInfo.blockSize = volumeHeader->blockSize;
volumeInfo.totalBlocks = volumeHeader->totalBlocks;
volumeInfo.freeBlocks = volumeHeader->freeBlocks;
volumeInfo.modifyDate = volumeHeader->modifyDate;
volumeInfo.signature = volumeHeader->signature;
volumeInfo.isHFS = TRUE;

result = buildDmgWithVolumeInfo(abstractIn, abstractOut, BlockSize, &volumeInfo);

closeVolume(volume);
CLOSE(io);

return result;
}

int convertToDMG(AbstractFile* abstractIn, AbstractFile* abstractOut) {
Partition* partitions;
DriverDescriptorRecord* DDM;
Expand Down
8 changes: 4 additions & 4 deletions src/resources.c
Original file line number Diff line number Diff line change
Expand Up @@ -858,17 +858,17 @@ ResourceKey* makePlst() {
return insertData(NULL, "plst", 0, "", plstData, sizeof(plstData), ATTRIBUTE_HDIUTIL);
}

ResourceKey* makeSize(HFSPlusVolumeHeader* volumeHeader) {
ResourceKey* makeSize(const DMGVolumeInfo* volumeInfo) {
SizeResource size;
memset(&size, 0, sizeof(SizeResource));
size.version = 5;
size.isHFS = 1;
size.isHFS = (volumeInfo && volumeInfo->isHFS) ? 1 : 0;
size.unknown1 = 0;
size.unknown2 = 0;
size.unknown3 = 0;
size.volumeModified = volumeHeader->modifyDate;
size.volumeModified = volumeInfo ? volumeInfo->modifyDate : 0;
size.unknown4 = 0;
size.volumeSignature = volumeHeader->signature;
size.volumeSignature = volumeInfo ? volumeInfo->signature : 0;
size.sizePresent = 1;

printf("making size data\n");
Expand Down