[attribute] Add `dmg build ... <sentinel>` and `dmg attribute ...`.
The new form of `dmg build` produces attributable DMGs and `dmg attribute` updates the attribution in place.
This commit is contained in:
Родитель
89b98b91b9
Коммит
5961355107
|
@ -1,3 +1,3 @@
|
|||
build
|
||||
/venv/
|
||||
/test/*.t
|
||||
/test/*.t.err
|
||||
|
|
|
@ -16,7 +16,7 @@ link_directories(${BZIP2_LIBRARIES})
|
|||
|
||||
link_directories(${PROJECT_BINARY_DIR}/common ${PROJECT_BINARY_DIR}/hfs)
|
||||
|
||||
add_library(dmg adc.c checksum.c dmgfile.c dmglib.c filevault.c io.c partition.c resources.c udif.c ../includes/dmg/adc.h ../includes/dmg/dmg.h ../includes/dmg/dmgfile.h ../includes/dmg/dmglib.h ../includes/dmg/filevault.h)
|
||||
add_library(dmg adc.c attribution.c checksum.c dmgfile.c dmglib.c filevault.c io.c partition.c resources.c udif.c ../includes/dmg/adc.h ../includes/dmg/attribution.h ../includes/dmg/dmg.h ../includes/dmg/dmgfile.h ../includes/dmg/dmglib.h ../includes/dmg/filevault.h)
|
||||
|
||||
IF(OPENSSL_FOUND)
|
||||
add_definitions(-DHAVE_CRYPT)
|
||||
|
|
|
@ -0,0 +1,327 @@
|
|||
#include <string.h>
|
||||
#include <zlib.h> // For crc32_combine.
|
||||
#include "abstractfile.h"
|
||||
#include "common.h"
|
||||
#include <dmg/attribution.h>
|
||||
#include <dmg/dmg.h>
|
||||
|
||||
typedef struct AttributionPreservingSentinelData {
|
||||
char* sentinel;
|
||||
|
||||
ChecksumToken beforeRaw_UncompressedChkToken;
|
||||
ChecksumToken raw_UncompressedChkToken;
|
||||
ChecksumToken afterRaw_UncompressedChkToken;
|
||||
int64_t beforeRaw_UncompressedLength;
|
||||
// -1 if sentinel (raw block) not yet seen.
|
||||
int64_t raw_UncompressedLength;
|
||||
int64_t afterRaw_UncompressedLength;
|
||||
|
||||
ChecksumToken beforeRaw_CompressedChkToken;
|
||||
ChecksumToken raw_CompressedChkToken;
|
||||
ChecksumToken afterRaw_CompressedChkToken;
|
||||
int64_t beforeRaw_CompressedLength;
|
||||
// -1 if sentinel (raw block) not yet seen.
|
||||
int64_t raw_CompressedLength;
|
||||
int64_t afterRaw_CompressedLength;
|
||||
} AttributionPreservingSentinelData;
|
||||
|
||||
char const *
|
||||
FindStrInBuf(char const * buf, size_t bufLen, char const * str)
|
||||
{
|
||||
size_t index = 0;
|
||||
while (index < bufLen) {
|
||||
char const * result = strstr(buf + index, str);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
while ((buf[index] != '\0') && (index < bufLen)) {
|
||||
index++;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int sentinelShouldKeepRaw(AbstractAttribution* attribution, const void* data, size_t len) {
|
||||
AttributionPreservingSentinelData* attributionData = (AttributionPreservingSentinelData*)attribution->data;
|
||||
/* return FALSE; */
|
||||
return NULL != FindStrInBuf((char const*)data, len, (const char*)attributionData->sentinel);
|
||||
}
|
||||
|
||||
void sentinelBeforeMainBlkx(AbstractAttribution* attribution, AbstractFile* abstractOut, ChecksumToken* dataForkToken) {
|
||||
|
||||
AttributionPreservingSentinelData* attributionData = (AttributionPreservingSentinelData*)attribution->data;
|
||||
memcpy(&attributionData->beforeRaw_CompressedChkToken, dataForkToken, sizeof(ChecksumToken));
|
||||
attributionData->beforeRaw_CompressedLength = abstractOut->tell(abstractOut);
|
||||
}
|
||||
|
||||
void sentinelObserveBuffers(AbstractAttribution* attribution, int didKeepRaw, const void* uncompressedData, size_t uncompressedLen, const void* compressedData, size_t compressedLen) {
|
||||
AttributionPreservingSentinelData* attributionData = (AttributionPreservingSentinelData*)attribution->data;
|
||||
|
||||
if (didKeepRaw) {
|
||||
ASSERT(attributionData->raw_UncompressedLength == -1, "Only one raw block supported!");
|
||||
ASSERT(attributionData->raw_CompressedLength == -1, "Only one raw block supported!");
|
||||
|
||||
// Just in case.
|
||||
memset(&attributionData->raw_UncompressedChkToken, 0, sizeof(ChecksumToken));
|
||||
memset(&attributionData->raw_CompressedChkToken, 0, sizeof(ChecksumToken));
|
||||
|
||||
CRCProxy(&attributionData->raw_UncompressedChkToken, uncompressedData, uncompressedLen);
|
||||
attributionData->raw_UncompressedLength = uncompressedLen;
|
||||
printf("Adding to raw_UncompressedChkToken, now CRC32: %x, length: %llx\n", attributionData->raw_UncompressedChkToken.crc, attributionData->raw_UncompressedLength);
|
||||
|
||||
// Of course, the compressed data *should* be the uncompressed data.
|
||||
CRCProxy(&attributionData->raw_CompressedChkToken, compressedData, compressedLen);
|
||||
attributionData->raw_CompressedLength = compressedLen;
|
||||
printf("Adding to raw_CompressedChkToken, now CRC32: %x, length: %llx\n", attributionData->raw_CompressedChkToken.crc, attributionData->raw_CompressedLength);
|
||||
|
||||
ASSERT(attributionData->afterRaw_UncompressedLength == -1, "Only one raw block supported!");
|
||||
ASSERT(attributionData->afterRaw_CompressedLength == -1, "Only one raw block supported!");
|
||||
|
||||
attributionData->afterRaw_UncompressedLength = 0;
|
||||
attributionData->afterRaw_CompressedLength = 0;
|
||||
// Just in case.
|
||||
memset(&attributionData->afterRaw_UncompressedChkToken, 0, sizeof(ChecksumToken));
|
||||
memset(&attributionData->afterRaw_CompressedChkToken, 0, sizeof(ChecksumToken));
|
||||
} else {
|
||||
if (attributionData->afterRaw_UncompressedLength < 0) {
|
||||
CRCProxy(&attributionData->beforeRaw_UncompressedChkToken, uncompressedData, uncompressedLen);
|
||||
attributionData->beforeRaw_UncompressedLength += uncompressedLen;
|
||||
printf("Adding to beforeRaw_UncompressedChkToken, now CRC32: %x, length: %llx\n", attributionData->beforeRaw_UncompressedChkToken.crc, attributionData->beforeRaw_UncompressedLength);
|
||||
} else {
|
||||
CRCProxy(&attributionData->afterRaw_UncompressedChkToken, uncompressedData, uncompressedLen);
|
||||
attributionData->afterRaw_UncompressedLength += uncompressedLen;
|
||||
printf("Adding to afterRaw_UncompressedChkToken, now CRC32: %x, length: %llx\n", attributionData->afterRaw_UncompressedChkToken.crc, attributionData->afterRaw_UncompressedLength);
|
||||
}
|
||||
|
||||
if (attributionData->afterRaw_CompressedLength < 0) {
|
||||
CRCProxy(&attributionData->beforeRaw_CompressedChkToken, compressedData, compressedLen);
|
||||
attributionData->beforeRaw_CompressedLength += compressedLen;
|
||||
printf("Adding to beforeRaw_CompressedChkToken, now CRC32: %x, length: %llx\n", attributionData->beforeRaw_CompressedChkToken.crc, attributionData->beforeRaw_CompressedLength);
|
||||
} else {
|
||||
CRCProxy(&attributionData->afterRaw_CompressedChkToken, compressedData, compressedLen);
|
||||
attributionData->afterRaw_CompressedLength += compressedLen;
|
||||
printf("Adding to afterRaw_CompressedChkToken, now CRC32: %x, length: %llx\n", attributionData->afterRaw_CompressedChkToken.crc, attributionData->afterRaw_CompressedLength);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void sentinelAfterMainBlkx(AbstractAttribution* attribution, AbstractFile* abstractOut, ChecksumToken* dataForkToken, AttributionResource* attributionResource) {
|
||||
AttributionPreservingSentinelData* data = attribution->data;
|
||||
|
||||
// Sentinels no longer make sense.
|
||||
if (data->raw_UncompressedLength < 0) {
|
||||
data->raw_UncompressedLength = 0;
|
||||
}
|
||||
if (data->afterRaw_UncompressedLength < 0) {
|
||||
data->afterRaw_UncompressedLength = 0;
|
||||
}
|
||||
if (data->raw_CompressedLength < 0) {
|
||||
data->raw_CompressedLength = 0;
|
||||
}
|
||||
if (data->afterRaw_CompressedLength < 0) {
|
||||
data->afterRaw_CompressedLength = 0;
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
printf("data->beforeRaw_UncompressedChkToken CRC32: 0x%x, length: 0x%llx\n", data->beforeRaw_UncompressedChkToken.crc, data->beforeRaw_UncompressedLength);
|
||||
printf("data->raw_UncompressedChkToken CRC32: 0x%x, length: 0x%llx\n", data->raw_UncompressedChkToken.crc, data->raw_UncompressedLength);
|
||||
printf("data->afterRaw_UncompressedChkToken CRC32: 0x%x, length: 0x%llx\n", data->afterRaw_UncompressedChkToken.crc, data->afterRaw_UncompressedLength);
|
||||
|
||||
printf("uncompressed combined CRC32: 0x%lx\n",
|
||||
crc32_combine(crc32_combine(data->beforeRaw_UncompressedChkToken.crc, data->raw_UncompressedChkToken.crc, data->raw_UncompressedLength),
|
||||
data->afterRaw_UncompressedChkToken.crc, data->afterRaw_UncompressedLength));
|
||||
|
||||
printf("\n");
|
||||
printf("data->beforeRaw_CompressedChkToken CRC32: 0x%x, length: 0x%llx\n", data->beforeRaw_CompressedChkToken.crc, data->beforeRaw_CompressedLength);
|
||||
printf("data->raw_CompressedChkToken CRC32: 0x%x, length: 0x%llx\n", data->raw_CompressedChkToken.crc, data->raw_CompressedLength);
|
||||
printf("data->afterRaw_CompressedChkToken CRC32: 0x%x, length: 0x%llx\n", data->afterRaw_CompressedChkToken.crc, data->afterRaw_CompressedLength);
|
||||
|
||||
printf("compressed combined CRC32: 0x%lx\n\n",
|
||||
crc32_combine(crc32_combine(data->beforeRaw_CompressedChkToken.crc, data->raw_CompressedChkToken.crc, data->raw_CompressedLength),
|
||||
data->afterRaw_CompressedChkToken.crc, data->afterRaw_CompressedLength));
|
||||
|
||||
// Update attribution metadata.
|
||||
attributionResource->signature = ATTR_SIGNATURE;
|
||||
attributionResource->version = 1;
|
||||
|
||||
attributionResource->beforeCompressedLength = data->beforeRaw_CompressedLength;
|
||||
attributionResource->beforeCompressedChecksum = data->beforeRaw_CompressedChkToken.crc;
|
||||
attributionResource->beforeUncompressedLength = data->beforeRaw_UncompressedLength;
|
||||
attributionResource->beforeUncompressedChecksum = data->beforeRaw_UncompressedChkToken.crc;
|
||||
|
||||
attributionResource->rawPos = data->beforeRaw_CompressedLength;
|
||||
attributionResource->rawLength = data->raw_CompressedLength;
|
||||
attributionResource->rawChecksum = data->raw_CompressedChkToken.crc;
|
||||
|
||||
attributionResource->afterCompressedLength = data->afterRaw_CompressedLength;
|
||||
attributionResource->afterCompressedChecksum = data->afterRaw_CompressedChkToken.crc;
|
||||
attributionResource->afterUncompressedLength = data->afterRaw_UncompressedLength;
|
||||
attributionResource->afterUncompressedChecksum = data->afterRaw_UncompressedChkToken.crc;
|
||||
|
||||
printf("sentinelAfterMainBlkx: %d, 0x%llx, 0x%llx\n", attributionResource->version, attributionResource->rawPos, attributionResource->rawLength);
|
||||
}
|
||||
|
||||
AbstractAttribution* createAbstractAttributionPreservingSentinel(const char* sentinel) {
|
||||
AbstractAttribution* attribution;
|
||||
attribution = (AbstractAttribution*) malloc(sizeof(AbstractAttribution));
|
||||
|
||||
AttributionPreservingSentinelData* data = malloc(sizeof(AttributionPreservingSentinelData));
|
||||
memset(data, 0, sizeof(AttributionPreservingSentinelData));
|
||||
data->sentinel = malloc(strlen(sentinel));
|
||||
strcpy(data->sentinel, sentinel);
|
||||
data->raw_UncompressedLength = -1;
|
||||
data->afterRaw_UncompressedLength = -1;
|
||||
data->raw_CompressedLength = -1;
|
||||
data->afterRaw_CompressedLength = -1;
|
||||
|
||||
attribution->data = data;
|
||||
attribution->beforeMainBlkx = sentinelBeforeMainBlkx;
|
||||
attribution->shouldKeepRaw = sentinelShouldKeepRaw;
|
||||
attribution->observeBuffers = sentinelObserveBuffers;
|
||||
attribution->afterMainBlkx = sentinelAfterMainBlkx;
|
||||
return attribution;
|
||||
}
|
||||
|
||||
uint32_t calculateMasterChecksum(ResourceKey* resources);
|
||||
|
||||
int updateAttribution(AbstractFile* abstractIn, AbstractFile* abstractOut, const char* anchor, const char* data, size_t dataLen)
|
||||
{
|
||||
// In an `attributable` DMG file:
|
||||
// - read `attribution` resource
|
||||
// - update bytes in BZ_RAW block in place
|
||||
// - update <blkx> checksum: there's a UDIF checksum (34 bytes?) in
|
||||
// each <blkx> dict, which is part of a Base64 encoded struct. We
|
||||
// can Base64 decode to bytes, swizzle the 4 bytes of the
|
||||
// checksum, and then Base64 encode back to the same number of
|
||||
// bytes.
|
||||
// - update data fork checksum (compressed)
|
||||
// - update master checksum (uncompressed)
|
||||
|
||||
off_t fileLength;
|
||||
UDIFResourceFile resourceFile;
|
||||
|
||||
ResourceData* curData;
|
||||
|
||||
fileLength = abstractIn->getLength(abstractIn);
|
||||
abstractIn->seek(abstractIn, fileLength - sizeof(UDIFResourceFile));
|
||||
readUDIFResourceFile(abstractIn, &resourceFile);
|
||||
|
||||
char* resourceXML;
|
||||
|
||||
resourceXML = malloc(resourceFile.fUDIFXMLLength + 1);
|
||||
ASSERT( abstractIn->seek(abstractIn, (off_t)(resourceFile.fUDIFXMLOffset)) == 0, "fseeko" );
|
||||
ASSERT( abstractIn->read(abstractIn, resourceXML, (size_t)resourceFile.fUDIFXMLLength) == (size_t)resourceFile.fUDIFXMLLength, "fread" );
|
||||
resourceXML[resourceFile.fUDIFXMLLength] = 0;
|
||||
|
||||
ResourceKey* resources;
|
||||
resources = readResources(resourceXML, resourceFile.fUDIFXMLLength, true);
|
||||
ResourceKey* resource = getResourceByKey(resources, "plst");
|
||||
unsigned char* mine = (unsigned char*)(resource->data->name);
|
||||
AttributionResource* attributionResource = (AttributionResource*)(resource->data->name);
|
||||
|
||||
ASSERT(attributionResource->signature == ATTR_SIGNATURE, "bad attr signature!");
|
||||
ASSERT(attributionResource->version == 1, "only version 1 recognized");
|
||||
|
||||
printf("attribution: at 0x%llx, 0x%llx bytes\n",
|
||||
attributionResource->rawPos,
|
||||
attributionResource->rawLength);
|
||||
|
||||
// Step 1. Replace bytes at anchor.
|
||||
ASSERT(abstractIn->seek(abstractIn, 0) == 0, "seek in");
|
||||
size_t inLength = abstractIn->getLength(abstractIn);
|
||||
while (1) {
|
||||
unsigned char buffer[8192];
|
||||
size_t readLength = abstractIn->read(abstractIn, buffer, 8192);
|
||||
ASSERT(readLength == abstractOut->write(abstractOut, buffer, readLength), "write copy");
|
||||
if (readLength < 8192) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT(abstractIn->seek(abstractIn, attributionResource->rawPos) == 0, "seek in");
|
||||
char* rawBuffer = malloc(attributionResource->rawLength);
|
||||
ASSERT(rawBuffer, "malloc rawBuffer");
|
||||
ASSERT(abstractIn->read(abstractIn, rawBuffer, attributionResource->rawLength) == attributionResource->rawLength, "read raw in");
|
||||
|
||||
printf("Looking for anchor: '%s'\n", anchor);
|
||||
|
||||
const char* rawAnchor = FindStrInBuf((const char*)rawBuffer, attributionResource->rawLength, anchor);
|
||||
ASSERT(rawAnchor, "anchor position");
|
||||
|
||||
int64_t anchorOffset = rawAnchor - rawBuffer;
|
||||
printf("anchorOffset: 0x%llx\n", anchorOffset);
|
||||
|
||||
ASSERT(rawAnchor + dataLen <= rawBuffer + attributionResource->rawLength, "data too long!");
|
||||
memcpy((void *)rawAnchor, data, dataLen);
|
||||
|
||||
// Write the new block.
|
||||
ASSERT(abstractOut->seek(abstractOut, attributionResource->rawPos) == 0, "seek out");
|
||||
ASSERT(abstractOut->write(abstractOut, rawBuffer, attributionResource->rawLength) == attributionResource->rawLength, "write data");
|
||||
|
||||
// New block checksum.
|
||||
ChecksumToken newRawToken;
|
||||
memset(&newRawToken, 0, sizeof(ChecksumToken));
|
||||
CRCProxy(&newRawToken, (unsigned char*)rawBuffer, attributionResource->rawLength);
|
||||
|
||||
free(rawBuffer);
|
||||
|
||||
// Step 2: update "attribution" resource.
|
||||
attributionResource->rawChecksum = newRawToken.crc;
|
||||
|
||||
// Step 3. Update blkx checksum.
|
||||
|
||||
// TODO: check ranges of all the partitions and runs to be sure we're identifying the correct file system.
|
||||
int partNum = -1;
|
||||
ResourceData* blkxData = NULL;
|
||||
if(partNum < 0) {
|
||||
blkxData = getResourceByKey(resources, "blkx")->data;
|
||||
while(blkxData != NULL) {
|
||||
if(strstr(blkxData->name, "Apple_HFS") != NULL) {
|
||||
break;
|
||||
}
|
||||
blkxData = blkxData->next;
|
||||
}
|
||||
} else {
|
||||
blkxData = getDataByID(getResourceByKey(resources, "blkx"), partNum);
|
||||
}
|
||||
|
||||
ASSERT(blkxData, "blkxData");
|
||||
|
||||
BLKXTable* blkx = (BLKXTable*)(blkxData->data);
|
||||
blkx->checksum.data[0] =
|
||||
crc32_combine(crc32_combine(attributionResource->beforeUncompressedChecksum, newRawToken.crc, attributionResource->rawLength),
|
||||
attributionResource->afterUncompressedChecksum, attributionResource->afterUncompressedLength);
|
||||
|
||||
ASSERT( abstractOut->seek(abstractOut, (off_t)(resourceFile.fUDIFXMLOffset)) == 0, "fseeko" );
|
||||
writeResources(abstractOut, resources, true);
|
||||
|
||||
// Step 4. Update koly block checksums.
|
||||
|
||||
resourceFile.fUDIFDataForkChecksum.type = CHECKSUM_UDIF_CRC32;
|
||||
resourceFile.fUDIFDataForkChecksum.bitness = checksumBitness(CHECKSUM_UDIF_CRC32);
|
||||
resourceFile.fUDIFDataForkChecksum.data[0] =
|
||||
crc32_combine(crc32_combine(attributionResource->beforeCompressedChecksum, newRawToken.crc, attributionResource->rawLength),
|
||||
attributionResource->afterCompressedChecksum, attributionResource->afterCompressedLength);
|
||||
|
||||
resourceFile.fUDIFMasterChecksum.type = CHECKSUM_UDIF_CRC32;
|
||||
resourceFile.fUDIFMasterChecksum.bitness = checksumBitness(CHECKSUM_UDIF_CRC32);
|
||||
resourceFile.fUDIFMasterChecksum.data[0] = calculateMasterChecksum(resources);
|
||||
printf("Master checksum: %x\n", resourceFile.fUDIFMasterChecksum.data[0]); fflush(stdout);
|
||||
|
||||
printf("Writing out UDIF resource file...\n"); fflush(stdout);
|
||||
|
||||
writeUDIFResourceFile(abstractOut, &resourceFile);
|
||||
|
||||
printf("Cleaning up...\n"); fflush(stdout);
|
||||
|
||||
releaseResources(resources);
|
||||
|
||||
abstractIn->close(abstractIn);
|
||||
|
||||
printf("Done\n"); fflush(stdout);
|
||||
|
||||
abstractOut->close(abstractOut);
|
||||
|
||||
return TRUE;
|
||||
}
|
11
dmg/dmg.c
11
dmg/dmg.c
|
@ -41,7 +41,7 @@ int main(int argc, char* argv[]) {
|
|||
TestByteOrder();
|
||||
|
||||
if(argc < 4) {
|
||||
printf("usage: %s [extract|build|build2048|res|iso|dmg] <in> <out> (-k <key>) (partition)\n", argv[0]);
|
||||
printf("usage: %s [extract|build|build2048|res|iso|dmg|attribute] <in> <out> (-k <key>) (partition)\n", argv[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -72,15 +72,20 @@ int main(int argc, char* argv[]) {
|
|||
}
|
||||
extractDmg(in, out, partNum);
|
||||
} else if(strcmp(argv[1], "build") == 0) {
|
||||
buildDmg(in, out, SECTOR_SIZE);
|
||||
buildDmg(in, out, SECTOR_SIZE, argc > 4 ? argv[4] : NULL);
|
||||
} else if(strcmp(argv[1], "build2048") == 0) {
|
||||
buildDmg(in, out, 2048);
|
||||
buildDmg(in, out, 2048, NULL);
|
||||
} else if(strcmp(argv[1], "res") == 0) {
|
||||
outResources(in, out);
|
||||
} else if(strcmp(argv[1], "iso") == 0) {
|
||||
convertToISO(in, out);
|
||||
} else if(strcmp(argv[1], "dmg") == 0) {
|
||||
convertToDMG(in, out);
|
||||
} else if(strcmp(argv[1], "attribute") == 0) {
|
||||
if(argc < 6) {
|
||||
printf("Not enough arguments: attribute <in> <out> <sentinel> <string>");
|
||||
}
|
||||
updateAttribution(in, out, argv[4], argv[5], strlen(argv[5]));
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -209,7 +209,7 @@ io_func* openDmgFile(AbstractFile* abstractIn) {
|
|||
ASSERT( abstractIn->read(abstractIn, dmg->resourceXML, (size_t)dmg->resourceFile.fUDIFXMLLength) == (size_t)dmg->resourceFile.fUDIFXMLLength, "fread" );
|
||||
dmg->resourceXML[dmg->resourceFile.fUDIFXMLLength] = 0;
|
||||
|
||||
dmg->resources = readResources(dmg->resourceXML, dmg->resourceFile.fUDIFXMLLength);
|
||||
dmg->resources = readResources(dmg->resourceXML, dmg->resourceFile.fUDIFXMLLength, true);
|
||||
dmg->numBLKX = 0;
|
||||
|
||||
blkx = (getResourceByKey(dmg->resources, "blkx"))->data;
|
||||
|
|
48
dmg/dmglib.c
48
dmg/dmglib.c
|
@ -1,6 +1,7 @@
|
|||
#include <string.h>
|
||||
#include "common.h"
|
||||
#include "abstractfile.h"
|
||||
#include <dmg/attribution.h>
|
||||
#include <dmg/dmg.h>
|
||||
#include <dmg/dmgfile.h>
|
||||
|
||||
|
@ -87,7 +88,7 @@ uint32_t calculateMasterChecksum(ResourceKey* resources) {
|
|||
return result;
|
||||
}
|
||||
|
||||
int buildDmg(AbstractFile* abstractIn, AbstractFile* abstractOut, unsigned int BlockSize) {
|
||||
int buildDmg(AbstractFile* abstractIn, AbstractFile* abstractOut, unsigned int BlockSize, const char* sentinel) {
|
||||
io_func* io;
|
||||
Volume* volume;
|
||||
|
||||
|
@ -147,15 +148,32 @@ int buildDmg(AbstractFile* abstractIn, AbstractFile* abstractOut, unsigned int B
|
|||
printf("Writing main data blkx...\n"); fflush(stdout);
|
||||
|
||||
abstractIn->seek(abstractIn, 0);
|
||||
|
||||
AbstractAttribution* attribution = NULL;
|
||||
if (sentinel) {
|
||||
attribution = createAbstractAttributionPreservingSentinel(sentinel);
|
||||
}
|
||||
|
||||
if (attribution) {
|
||||
attribution->beforeMainBlkx(attribution, abstractOut, &dataForkToken);
|
||||
}
|
||||
|
||||
blkx = insertBLKX(abstractOut, abstractIn, USER_OFFSET, (volumeHeader->totalBlocks * volumeHeader->blockSize)/SECTOR_SIZE,
|
||||
pNum, CHECKSUM_UDIF_CRC32, &BlockSHA1CRC, &uncompressedToken, &CRCProxy, &dataForkToken, volume, 0);
|
||||
pNum, CHECKSUM_UDIF_CRC32, &BlockSHA1CRC, &uncompressedToken, &CRCProxy, &dataForkToken, volume, 0, attribution);
|
||||
|
||||
AttributionResource attributionResource;
|
||||
memset(&attributionResource, 0, sizeof(AttributionResource));
|
||||
|
||||
if (attribution) {
|
||||
attribution->afterMainBlkx(attribution, abstractOut, &dataForkToken, &attributionResource);
|
||||
}
|
||||
|
||||
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);
|
||||
resources = insertData(resources, "blkx", pNum, pName, (const char*) blkx, sizeof(BLKXTable) + (blkx->blocksRunCount * sizeof(BLKXRun)), ATTRIBUTE_HDIUTIL);
|
||||
resources = insertData(resources, "blkx", pNum, pName, 0, false, (const char*) blkx, sizeof(BLKXTable) + (blkx->blocksRunCount * sizeof(BLKXRun)), ATTRIBUTE_HDIUTIL);
|
||||
free(blkx);
|
||||
|
||||
printf("Inserting cSum data...\n"); fflush(stdout);
|
||||
|
@ -164,7 +182,7 @@ int buildDmg(AbstractFile* abstractIn, AbstractFile* abstractOut, unsigned int B
|
|||
csum.type = CHECKSUM_MKBLOCK;
|
||||
csum.checksum = uncompressedToken.block;
|
||||
|
||||
resources = insertData(resources, "cSum", 2, "", (const char*) (&csum), sizeof(csum), 0);
|
||||
resources = insertData(resources, "cSum", 2, "", 0, false, (const char*) (&csum), sizeof(csum), 0);
|
||||
|
||||
printf("Inserting nsiz data\n"); fflush(stdout);
|
||||
|
||||
|
@ -209,14 +227,14 @@ int buildDmg(AbstractFile* abstractIn, AbstractFile* abstractOut, unsigned int B
|
|||
curResource = curResource->next;
|
||||
releaseNSiz(nsiz);
|
||||
|
||||
curResource->next = makePlst();
|
||||
curResource->next = makePlst((const char*) (&attributionResource), sizeof(attributionResource), true);
|
||||
curResource = curResource->next;
|
||||
|
||||
curResource->next = makeSize(volumeHeader);
|
||||
curResource = curResource->next;
|
||||
|
||||
plistOffset = abstractOut->tell(abstractOut);
|
||||
writeResources(abstractOut, resources);
|
||||
writeResources(abstractOut, resources, true);
|
||||
plistSize = abstractOut->tell(abstractOut) - plistOffset;
|
||||
|
||||
printf("Generating UDIF metadata...\n"); fflush(stdout);
|
||||
|
@ -353,17 +371,17 @@ int convertToDMG(AbstractFile* abstractIn, AbstractFile* abstractOut) {
|
|||
|
||||
abstractIn->seek(abstractIn, partitions[i].pmPyPartStart * BlockSize);
|
||||
blkx = insertBLKX(abstractOut, abstractIn, partitions[i].pmPyPartStart, partitions[i].pmPartBlkCnt, i, CHECKSUM_UDIF_CRC32,
|
||||
&BlockCRC, &uncompressedToken, &CRCProxy, &dataForkToken, NULL, 0);
|
||||
&BlockCRC, &uncompressedToken, &CRCProxy, &dataForkToken, NULL, 0, NULL);
|
||||
|
||||
blkx->checksum.data[0] = uncompressedToken.crc;
|
||||
resources = insertData(resources, "blkx", i, partitionName, (const char*) blkx, sizeof(BLKXTable) + (blkx->blocksRunCount * sizeof(BLKXRun)), ATTRIBUTE_HDIUTIL);
|
||||
resources = insertData(resources, "blkx", i, partitionName, 0, false, (const char*) blkx, sizeof(BLKXTable) + (blkx->blocksRunCount * sizeof(BLKXRun)), ATTRIBUTE_HDIUTIL);
|
||||
free(blkx);
|
||||
|
||||
memset(&csum, 0, sizeof(CSumResource));
|
||||
csum.version = 1;
|
||||
csum.type = CHECKSUM_MKBLOCK;
|
||||
csum.checksum = uncompressedToken.block;
|
||||
resources = insertData(resources, "cSum", i, "", (const char*) (&csum), sizeof(csum), 0);
|
||||
resources = insertData(resources, "cSum", i, "", 0, false, (const char*) (&csum), sizeof(csum), 0);
|
||||
|
||||
if(nsiz == NULL) {
|
||||
nsiz = myNSiz = (NSizResource*) malloc(sizeof(NSizResource));
|
||||
|
@ -394,16 +412,16 @@ int convertToDMG(AbstractFile* abstractIn, AbstractFile* abstractOut) {
|
|||
|
||||
abstractIn->seek(abstractIn, 0);
|
||||
blkx = insertBLKX(abstractOut, abstractIn, 0, fileLength/SECTOR_SIZE, ENTIRE_DEVICE_DESCRIPTOR, CHECKSUM_UDIF_CRC32,
|
||||
&BlockCRC, &uncompressedToken, &CRCProxy, &dataForkToken, NULL, 0);
|
||||
&BlockCRC, &uncompressedToken, &CRCProxy, &dataForkToken, NULL, 0, NULL);
|
||||
blkx->checksum.data[0] = uncompressedToken.crc;
|
||||
resources = insertData(resources, "blkx", 0, "whole disk (unknown partition : 0)", (const char*) blkx, sizeof(BLKXTable) + (blkx->blocksRunCount * sizeof(BLKXRun)), ATTRIBUTE_HDIUTIL);
|
||||
resources = insertData(resources, "blkx", 0, "whole disk (unknown partition : 0)", 0, false, (const char*) blkx, sizeof(BLKXTable) + (blkx->blocksRunCount * sizeof(BLKXRun)), ATTRIBUTE_HDIUTIL);
|
||||
free(blkx);
|
||||
|
||||
memset(&csum, 0, sizeof(CSumResource));
|
||||
csum.version = 1;
|
||||
csum.type = CHECKSUM_MKBLOCK;
|
||||
csum.checksum = uncompressedToken.block;
|
||||
resources = insertData(resources, "cSum", 0, "", (const char*) (&csum), sizeof(csum), 0);
|
||||
resources = insertData(resources, "cSum", 0, "", 0, false, (const char*) (&csum), sizeof(csum), 0);
|
||||
|
||||
if(nsiz == NULL) {
|
||||
nsiz = myNSiz = (NSizResource*) malloc(sizeof(NSizResource));
|
||||
|
@ -433,11 +451,13 @@ int convertToDMG(AbstractFile* abstractIn, AbstractFile* abstractOut) {
|
|||
curResource = curResource->next;
|
||||
releaseNSiz(nsiz);
|
||||
|
||||
curResource->next = makePlst();
|
||||
curResource->next = makePlst("", 0, false);
|
||||
curResource = curResource->next;
|
||||
|
||||
plistOffset = abstractOut->tell(abstractOut);
|
||||
writeResources(abstractOut, resources);
|
||||
// Note: Passing false here means that attribution data is not preserved through
|
||||
// a `dmg convert` operation.
|
||||
writeResources(abstractOut, resources, false);
|
||||
plistSize = abstractOut->tell(abstractOut) - plistOffset;
|
||||
|
||||
printf("Generating UDIF metadata...\n"); fflush(stdout);
|
||||
|
|
30
dmg/io.c
30
dmg/io.c
|
@ -23,7 +23,7 @@
|
|||
|
||||
BLKXTable* insertBLKX(AbstractFile* out, AbstractFile* in, uint32_t firstSectorNumber, uint32_t numSectors, uint32_t blocksDescriptor,
|
||||
uint32_t checksumType, ChecksumFunc uncompressedChk, void* uncompressedChkToken, ChecksumFunc compressedChk,
|
||||
void* compressedChkToken, Volume* volume, int addComment) {
|
||||
void* compressedChkToken, Volume* volume, int addComment, AbstractAttribution* attribution) {
|
||||
BLKXTable* blkx;
|
||||
|
||||
uint32_t roomForRuns;
|
||||
|
@ -72,6 +72,11 @@ BLKXTable* insertBLKX(AbstractFile* out, AbstractFile* in, uint32_t firstSectorN
|
|||
|
||||
uint64_t startOff = in->tell(in);
|
||||
|
||||
// We never want the iOS-specific tweaks when building attributable DMGs.
|
||||
if (attribution) {
|
||||
ASSERT(!addComment, "No attribution with addComment!");
|
||||
}
|
||||
|
||||
if(addComment)
|
||||
{
|
||||
blkx->runs[curRun].type = BLOCK_COMMENT;
|
||||
|
@ -202,6 +207,13 @@ BLKXTable* insertBLKX(AbstractFile* out, AbstractFile* in, uint32_t firstSectorN
|
|||
strm.avail_in = amountRead;
|
||||
strm.next_in = (char*)inBuffer;
|
||||
|
||||
bool keepRaw = FALSE;
|
||||
if (attribution) {
|
||||
// TODO: what about when sectors align badly?
|
||||
keepRaw = attribution->shouldKeepRaw(attribution, inBuffer, amountRead);
|
||||
printf("keepRaw = %d (%p, %d)\n", keepRaw, inBuffer, amountRead);
|
||||
}
|
||||
|
||||
if(uncompressedChk)
|
||||
(*uncompressedChk)(uncompressedChkToken, inBuffer, blkx->runs[curRun].sectorCount * SECTOR_SIZE);
|
||||
|
||||
|
@ -217,7 +229,8 @@ BLKXTable* insertBLKX(AbstractFile* out, AbstractFile* in, uint32_t firstSectorN
|
|||
}
|
||||
have = bufferSize - strm.avail_out;
|
||||
|
||||
if((have / SECTOR_SIZE) >= (blkx->runs[curRun].sectorCount - 15)) {
|
||||
if(keepRaw || ((have / SECTOR_SIZE) >= (blkx->runs[curRun].sectorCount - 15))) {
|
||||
printf("Setting type = BLOCK_RAW\n");
|
||||
blkx->runs[curRun].type = BLOCK_RAW;
|
||||
ASSERT(out->write(out, inBuffer, blkx->runs[curRun].sectorCount * SECTOR_SIZE) == (blkx->runs[curRun].sectorCount * SECTOR_SIZE), "fwrite");
|
||||
blkx->runs[curRun].compLength += blkx->runs[curRun].sectorCount * SECTOR_SIZE;
|
||||
|
@ -225,12 +238,25 @@ BLKXTable* insertBLKX(AbstractFile* out, AbstractFile* in, uint32_t firstSectorN
|
|||
if(compressedChk)
|
||||
(*compressedChk)(compressedChkToken, inBuffer, blkx->runs[curRun].sectorCount * SECTOR_SIZE);
|
||||
|
||||
if (attribution) {
|
||||
// In a raw block, uncompressed and compressed data is identical.
|
||||
attribution->observeBuffers(attribution, keepRaw,
|
||||
inBuffer, blkx->runs[curRun].sectorCount * SECTOR_SIZE,
|
||||
inBuffer, blkx->runs[curRun].sectorCount * SECTOR_SIZE);
|
||||
}
|
||||
} else {
|
||||
ASSERT(out->write(out, outBuffer, have) == have, "fwrite");
|
||||
|
||||
if(compressedChk)
|
||||
(*compressedChk)(compressedChkToken, outBuffer, have);
|
||||
|
||||
if (attribution) {
|
||||
// In a bzip2 block, uncompressed and compressed data are not the same.
|
||||
attribution->observeBuffers(attribution, keepRaw,
|
||||
inBuffer, amountRead,
|
||||
outBuffer, have);
|
||||
}
|
||||
|
||||
blkx->runs[curRun].compLength += have;
|
||||
}
|
||||
|
||||
|
|
|
@ -514,13 +514,13 @@ int writeDriverDescriptorMap(int pNum, AbstractFile* file, DriverDescriptorRecor
|
|||
bufferFile = createAbstractFileFromMemory((void**)&buffer, DDM_SIZE * BlockSize);
|
||||
|
||||
blkx = insertBLKX(file, bufferFile, DDM_OFFSET, DDM_SIZE, DDM_DESCRIPTOR, CHECKSUM_UDIF_CRC32, &CRCProxy, &uncompressedToken,
|
||||
dataForkChecksum, dataForkToken, NULL, 0);
|
||||
dataForkChecksum, dataForkToken, NULL, 0, NULL);
|
||||
|
||||
blkx->checksum.data[0] = uncompressedToken.crc;
|
||||
|
||||
char pName[100];
|
||||
sprintf(pName, "Driver Descriptor Map (DDM : %d)", pNum + 1);
|
||||
*resources = insertData(*resources, "blkx", pNum, pName, (const char*) blkx, sizeof(BLKXTable) + (blkx->blocksRunCount * sizeof(BLKXRun)), ATTRIBUTE_HDIUTIL);
|
||||
*resources = insertData(*resources, "blkx", pNum, pName, 0, false, (const char*) blkx, sizeof(BLKXTable) + (blkx->blocksRunCount * sizeof(BLKXRun)), ATTRIBUTE_HDIUTIL);
|
||||
|
||||
free(buffer);
|
||||
bufferFile->close(bufferFile);
|
||||
|
@ -552,7 +552,7 @@ int writeApplePartitionMap(int pNum, AbstractFile* file, Partition* partitions,
|
|||
bufferFile = createAbstractFileFromMemory((void**)&buffer, realPartitionSize);
|
||||
|
||||
blkx = insertBLKX(file, bufferFile, PARTITION_OFFSET * BlockSize / SECTOR_SIZE, realPartitionSize / SECTOR_SIZE, pNum, CHECKSUM_UDIF_CRC32,
|
||||
&BlockCRC, &uncompressedToken, dataForkChecksum, dataForkToken, NULL, 0);
|
||||
&BlockCRC, &uncompressedToken, dataForkChecksum, dataForkToken, NULL, 0, NULL);
|
||||
|
||||
bufferFile->close(bufferFile);
|
||||
|
||||
|
@ -564,8 +564,8 @@ int writeApplePartitionMap(int pNum, AbstractFile* file, Partition* partitions,
|
|||
|
||||
char pName[100];
|
||||
sprintf(pName, "Apple (Apple_partition_map : %d)", pNum + 1);
|
||||
*resources = insertData(*resources, "blkx", pNum, pName, (const char*) blkx, sizeof(BLKXTable) + (blkx->blocksRunCount * sizeof(BLKXRun)), ATTRIBUTE_HDIUTIL);
|
||||
*resources = insertData(*resources, "cSum", 0, "", (const char*) (&csum), sizeof(csum), 0);
|
||||
*resources = insertData(*resources, "blkx", pNum, pName, 0, false, (const char*) blkx, sizeof(BLKXTable) + (blkx->blocksRunCount * sizeof(BLKXRun)), ATTRIBUTE_HDIUTIL);
|
||||
*resources = insertData(*resources, "cSum", 0, "", 0, false, (const char*) (&csum), sizeof(csum), 0);
|
||||
|
||||
nsiz = (NSizResource*) malloc(sizeof(NSizResource));
|
||||
memset(nsiz, 0, sizeof(NSizResource));
|
||||
|
@ -606,12 +606,12 @@ int writeATAPI(int pNum, AbstractFile* file, unsigned int BlockSize, ChecksumFun
|
|||
if(BlockSize != SECTOR_SIZE)
|
||||
{
|
||||
blkx = insertBLKX(file, bufferFile, ATAPI_OFFSET, BlockSize / SECTOR_SIZE, pNum, CHECKSUM_UDIF_CRC32,
|
||||
&BlockCRC, &uncompressedToken, dataForkChecksum, dataForkToken, NULL, 0);
|
||||
&BlockCRC, &uncompressedToken, dataForkChecksum, dataForkToken, NULL, 0, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
blkx = insertBLKX(file, bufferFile, ATAPI_OFFSET, ATAPI_SIZE, pNum, CHECKSUM_UDIF_CRC32,
|
||||
&BlockCRC, &uncompressedToken, dataForkChecksum, dataForkToken, NULL, 0);
|
||||
&BlockCRC, &uncompressedToken, dataForkChecksum, dataForkToken, NULL, 0, NULL);
|
||||
}
|
||||
|
||||
bufferFile->close(bufferFile);
|
||||
|
@ -625,8 +625,8 @@ int writeATAPI(int pNum, AbstractFile* file, unsigned int BlockSize, ChecksumFun
|
|||
|
||||
char pName[100];
|
||||
sprintf(pName, "Macintosh (Apple_Driver_ATAPI : %d)", pNum + 1);
|
||||
*resources = insertData(*resources, "blkx", pNum, pName, (const char*) blkx, sizeof(BLKXTable) + (blkx->blocksRunCount * sizeof(BLKXRun)), ATTRIBUTE_HDIUTIL);
|
||||
*resources = insertData(*resources, "cSum", 1, "", (const char*) (&csum), sizeof(csum), 0);
|
||||
*resources = insertData(*resources, "blkx", pNum, pName, 0, false, (const char*) blkx, sizeof(BLKXTable) + (blkx->blocksRunCount * sizeof(BLKXRun)), ATTRIBUTE_HDIUTIL);
|
||||
*resources = insertData(*resources, "cSum", 1, "", 0, false, (const char*) (&csum), sizeof(csum), 0);
|
||||
|
||||
nsiz = (NSizResource*) malloc(sizeof(NSizResource));
|
||||
memset(nsiz, 0, sizeof(NSizResource));
|
||||
|
@ -835,7 +835,7 @@ int writeFreePartition(int pNum, AbstractFile* outFile, uint32_t offset, uint32_
|
|||
|
||||
char pName[100];
|
||||
sprintf(pName, " (Apple_Free : %d)", pNum + 1);
|
||||
*resources = insertData(*resources, "blkx", pNum, pName, (const char*) blkx, sizeof(BLKXTable) + (blkx->blocksRunCount * sizeof(BLKXRun)), ATTRIBUTE_HDIUTIL);
|
||||
*resources = insertData(*resources, "blkx", pNum, pName, 0, false, (const char*) blkx, sizeof(BLKXTable) + (blkx->blocksRunCount * sizeof(BLKXRun)), ATTRIBUTE_HDIUTIL);
|
||||
|
||||
free(blkx);
|
||||
return pNum + 1;
|
||||
|
|
103
dmg/resources.c
103
dmg/resources.c
|
@ -122,6 +122,24 @@ static void flipCSumResource(unsigned char* data, char out) {
|
|||
FLIPENDIANLE(cSum->checksum);
|
||||
}
|
||||
|
||||
static void flipAttributionResource(unsigned char* data, char out) {
|
||||
AttributionResource* attribution;
|
||||
attribution = (AttributionResource*) data;
|
||||
|
||||
FLIPENDIANLE(attribution->signature);
|
||||
FLIPENDIANLE(attribution->version);
|
||||
FLIPENDIANLE(attribution->rawPos);
|
||||
FLIPENDIANLE(attribution->rawLength);
|
||||
FLIPENDIANLE(attribution->rawChecksum);
|
||||
FLIPENDIANLE(attribution->beforeCompressedChecksum);
|
||||
FLIPENDIANLE(attribution->beforeCompressedLength);
|
||||
FLIPENDIANLE(attribution->beforeUncompressedChecksum);
|
||||
FLIPENDIANLE(attribution->beforeUncompressedLength);
|
||||
FLIPENDIANLE(attribution->afterCompressedChecksum);
|
||||
FLIPENDIANLE(attribution->afterCompressedLength);
|
||||
FLIPENDIANLE(attribution->afterUncompressedChecksum);
|
||||
FLIPENDIANLE(attribution->afterUncompressedLength);
|
||||
}
|
||||
|
||||
static void flipBLKXRun(BLKXRun* data) {
|
||||
BLKXRun* run;
|
||||
|
@ -286,7 +304,47 @@ static unsigned char* getXMLData(char** location, size_t *dataLength, char** enc
|
|||
return toReturn;
|
||||
}
|
||||
|
||||
static void readResourceData(ResourceData* data, char** location, char* xmlStart, FlipDataFunc flipData) {
|
||||
static unsigned char* getXMLPlstName(char** location, size_t *dataLength, char** encodedStart, size_t* encodedLength) {
|
||||
char* curLoc;
|
||||
char* tagEnd;
|
||||
char* encodedData;
|
||||
unsigned char* toReturn;
|
||||
size_t strLen;
|
||||
|
||||
curLoc = *location;
|
||||
|
||||
curLoc = strstr(curLoc, "<string>");
|
||||
if(!curLoc)
|
||||
return NULL;
|
||||
curLoc += sizeof("<string>") - 1;
|
||||
|
||||
if (encodedStart)
|
||||
*encodedStart = curLoc;
|
||||
|
||||
tagEnd = strstr(curLoc, "</string>");
|
||||
|
||||
|
||||
strLen = (size_t)(tagEnd - curLoc);
|
||||
|
||||
if (encodedLength)
|
||||
*encodedLength = strLen;
|
||||
|
||||
encodedData = (char*) malloc(strLen + 1);
|
||||
memcpy(encodedData, curLoc, strLen);
|
||||
encodedData[strLen] = '\0';
|
||||
|
||||
curLoc = tagEnd + sizeof("</string>") - 1;
|
||||
|
||||
*location = curLoc;
|
||||
|
||||
toReturn = decodeBase64(encodedData, dataLength);
|
||||
|
||||
free(encodedData);
|
||||
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
static void readResourceData(ResourceData* data, char** location, char* xmlStart, FlipDataFunc flipData, const unsigned char* key, bool plstNameIsAttribution) {
|
||||
char* curLoc;
|
||||
char* tagBegin;
|
||||
char* tagEnd;
|
||||
|
@ -332,9 +390,18 @@ static void readResourceData(ResourceData* data, char** location, char* xmlStart
|
|||
sscanf(buffer, "%d", &(data->id));
|
||||
free(buffer);
|
||||
} else if(strncmp(tagBegin, "Name", strLen) == 0) {
|
||||
if (strcmp((char*) key, "plst") == 0 && plstNameIsAttribution) {
|
||||
char *nameEncodedStart;
|
||||
size_t nameXmlSize;
|
||||
size_t nameLength;
|
||||
unsigned char* attributionFromName = getXMLPlstName(&curLoc, &nameLength, &nameEncodedStart, &nameXmlSize);
|
||||
data->name = attributionFromName;
|
||||
flipAttributionResource(data->name, 0);
|
||||
} else {
|
||||
data->name = getXMLString(&curLoc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
curLoc = dictEnd + sizeof("</dict>") - 1;
|
||||
|
||||
|
@ -552,7 +619,7 @@ void outResources(AbstractFile* file, AbstractFile* out)
|
|||
out->close(out);
|
||||
}
|
||||
|
||||
ResourceKey* readResources(char* xml, size_t length) {
|
||||
ResourceKey* readResources(char* xml, size_t length, bool plstNameIsAttribution) {
|
||||
char* curLoc;
|
||||
char* tagEnd;
|
||||
size_t strLen;
|
||||
|
@ -636,7 +703,7 @@ ResourceKey* readResources(char* xml, size_t length) {
|
|||
|
||||
curData->next = NULL;
|
||||
|
||||
readResourceData(curData, &curLoc, xml, curResource->flipData);
|
||||
readResourceData(curData, &curLoc, xml, curResource->flipData, curResource->key, plstNameIsAttribution);
|
||||
curLoc = strstr(curLoc, "<dict>");
|
||||
}
|
||||
|
||||
|
@ -646,7 +713,7 @@ ResourceKey* readResources(char* xml, size_t length) {
|
|||
return toReturn;
|
||||
}
|
||||
|
||||
static void writeResourceData(AbstractFile* file, ResourceData* data, ResourceKey* curResource, FlipDataFunc flipData, int tabLength) {
|
||||
static void writeResourceData(AbstractFile* file, ResourceData* data, ResourceKey* curResource, FlipDataFunc flipData, int tabLength, bool plstNameIsAttribution) {
|
||||
unsigned char* dataBuf;
|
||||
char* tabs;
|
||||
int i;
|
||||
|
@ -677,13 +744,23 @@ static void writeResourceData(AbstractFile* file, ResourceData* data, ResourceKe
|
|||
|
||||
abstractFilePrint(file, "%s\t</data>\n", tabs);
|
||||
abstractFilePrint(file, "%s\t<key>ID</key>\n%s\t<string>%d</string>\n", tabs, tabs, data->id);
|
||||
if (strcmp((char*) curResource->key, "plst") == 0 && plstNameIsAttribution) {
|
||||
unsigned char* nameBuf = (unsigned char*) malloc(sizeof(AttributionResource));
|
||||
memcpy(nameBuf, data->name, sizeof(AttributionResource));
|
||||
flipAttributionResource(nameBuf, 1);
|
||||
abstractFilePrint(file, "%s\t<key>Name</key>\n%s\t<string>\n", tabs, tabs);
|
||||
writeBase64(file, nameBuf, sizeof(AttributionResource), tabLength + 1, 43);
|
||||
abstractFilePrint(file, "%s\t</string>\n", tabs);
|
||||
}
|
||||
else {
|
||||
abstractFilePrint(file, "%s\t<key>Name</key>\n%s\t<string>%s</string>\n", tabs, tabs, data->name);
|
||||
}
|
||||
abstractFilePrint(file, "%s</dict>\n", tabs);
|
||||
|
||||
free(tabs);
|
||||
}
|
||||
|
||||
void writeResources(AbstractFile* file, ResourceKey* resources) {
|
||||
void writeResources(AbstractFile* file, ResourceKey* resources, bool plstNameIsAttribution) {
|
||||
ResourceKey* curResource;
|
||||
ResourceData* curData;
|
||||
|
||||
|
@ -695,7 +772,7 @@ void writeResources(AbstractFile* file, ResourceKey* resources) {
|
|||
abstractFilePrint(file, "\t\t<key>%s</key>\n\t\t<array>\n", curResource->key);
|
||||
curData = curResource->data;
|
||||
while(curData != NULL) {
|
||||
writeResourceData(file, curData, curResource, curResource->flipData, 3);
|
||||
writeResourceData(file, curData, curResource, curResource->flipData, 3, plstNameIsAttribution);
|
||||
curData = curData->next;
|
||||
}
|
||||
abstractFilePrint(file, "\t\t</array>\n", curResource->key);
|
||||
|
@ -769,7 +846,7 @@ ResourceData* getDataByID(ResourceKey* resource, int id) {
|
|||
return NULL;
|
||||
}
|
||||
|
||||
ResourceKey* insertData(ResourceKey* resources, const char* key, int id, const char* name, const char* data, size_t dataLength, uint32_t attributes) {
|
||||
ResourceKey* insertData(ResourceKey* resources, const char* key, int id, const char* name, size_t nameLength, bool nameAsData, const char* data, size_t dataLength, uint32_t attributes) {
|
||||
ResourceKey* curResource;
|
||||
ResourceKey* lastResource;
|
||||
ResourceData* curData;
|
||||
|
@ -836,8 +913,14 @@ ResourceKey* insertData(ResourceKey* resources, const char* key, int id, const c
|
|||
curData->attributes = attributes;
|
||||
curData->dataLength = dataLength;
|
||||
curData->id = id;
|
||||
if (nameAsData) {
|
||||
curData->name = (unsigned char*) malloc(nameLength);
|
||||
memcpy(curData->name, name, nameLength);
|
||||
}
|
||||
else {
|
||||
curData->name = (char*) malloc(strlen(name) + 1);
|
||||
strcpy((char*) curData->name, name);
|
||||
}
|
||||
curData->data = (unsigned char*) malloc(dataLength);
|
||||
memcpy(curData->data, data, dataLength);
|
||||
|
||||
|
@ -854,8 +937,8 @@ ResourceKey* insertData(ResourceKey* resources, const char* key, int id, const c
|
|||
}
|
||||
}
|
||||
|
||||
ResourceKey* makePlst() {
|
||||
return insertData(NULL, "plst", 0, "", plstData, sizeof(plstData), ATTRIBUTE_HDIUTIL);
|
||||
ResourceKey* makePlst(const char* name, size_t nameLength, bool nameAsData) {
|
||||
return insertData(NULL, "plst", 0, name, nameLength, nameAsData, plstData, sizeof(plstData), ATTRIBUTE_HDIUTIL);
|
||||
}
|
||||
|
||||
ResourceKey* makeSize(HFSPlusVolumeHeader* volumeHeader) {
|
||||
|
@ -872,6 +955,6 @@ ResourceKey* makeSize(HFSPlusVolumeHeader* volumeHeader) {
|
|||
size.sizePresent = 1;
|
||||
|
||||
printf("making size data\n");
|
||||
return insertData(NULL, "size", 2, "", (const char*)(&size), sizeof(SizeResource), 0);
|
||||
return insertData(NULL, "size", 2, "", 0, false, (const char*)(&size), sizeof(SizeResource), 0);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
# Attribution
|
||||
|
||||
`hfsplus hfsplus in.hfs setattr <file> <attr-key> <attr-value>`
|
||||
- sets an extended attribute on `file` with the given key and value
|
||||
|
||||
`dmg build in.hfs out.dmg <sentinel>`
|
||||
- arrange for the one (or two) blocks that the sentinel spans to be BZ_RAW
|
||||
- writes out `AttributionResource` in the `Name` key of the `plst` resource
|
||||
|
||||
`dmg attribute in.dmg out.dmg <sentinel> <replacement>`
|
||||
N.b. attribute shouldn't parse the DMG, it should operate on bytestream
|
||||
- read `attribution` resource
|
||||
- update bytes in BZ_RAW block in place
|
||||
- update <blkx> checksum
|
||||
- update data fork checksum (compressed)
|
||||
- update master checksum (uncompressed)
|
||||
|
||||
# Testing
|
||||
|
||||
The gold standard is to create two HFS filesystems, both with the same
|
||||
xattr but with distinct values, and build attributable DMGs with the
|
||||
same sentinel (a prefix of the distinct values, say).
|
||||
|
||||
See the `*_reference.t` tests for [Cram](https://pypi.org/project/cram/)
|
||||
tests that generate such HFS and DMG files.
|
||||
|
||||
See the other tests for tests that exercise `dmg build` and `dmg
|
||||
attribute`.
|
||||
|
||||
## Future
|
||||
|
||||
It would be nice to add a command to convert an existing DMG file to
|
||||
an attributable one, like:
|
||||
|
||||
`dmg attributable <in.dmg> <out.dmg> <sentinel>`
|
||||
- arrange for the one (or two) blocks that the sentinel spans to be BZ_RAW
|
||||
- writes out `AttributionResource` in the `Name` key of the `plst` resource
|
|
@ -5,10 +5,6 @@
|
|||
#define ADC_2BYTE 0x02
|
||||
#define ADC_3BYTE 0x03
|
||||
|
||||
#define bool short
|
||||
#define true 1
|
||||
#define false 0
|
||||
|
||||
size_t adc_decompress(size_t in_size, unsigned char *input, size_t avail_size, unsigned char *output, size_t *bytes_written);
|
||||
int adc_chunk_type(char _byte);
|
||||
int adc_chunk_size(char _byte);
|
||||
|
|
|
@ -0,0 +1,115 @@
|
|||
#ifndef ATTRIBUTION_H
|
||||
#define ATTRIBUTION_H
|
||||
|
||||
#include "abstractfile.h"
|
||||
#include "common.h"
|
||||
#include <stdint.h>
|
||||
|
||||
#define ATTR_SIGNATURE 0x61747472 // 'attr'
|
||||
|
||||
/**
|
||||
* An "attributable" DMG or a DMG "structured for attribution" has:
|
||||
* - either one or two BZ_RAW raw blocks
|
||||
* - a serialized form of the `AttributionResource` data structure
|
||||
* tucked into the `Name` in the `plst` section of the XML plist
|
||||
* which describes the raw block
|
||||
*
|
||||
* An "attributable" DMG can be "attributed" inexpensively: bytes in the raw
|
||||
* block of an "attributable" DMG can be changed and the DMG's internal
|
||||
* checksums updated without parsing and/or decompressing the entire DMG file.
|
||||
*/
|
||||
|
||||
// We need:
|
||||
// - the BZ_RAW block offset and size
|
||||
// - the CRCs before and after the BZ_RAW block
|
||||
// - the BZIP checksum offset
|
||||
// - the two DMG 'koly' block checksum offsets
|
||||
// fUDIFDataForkChecksum: 0x430e7
|
||||
// fUDIFMasterChecksum: 0x431f7
|
||||
// There's a UDIF checksum (34 bytes?) in each <blkx> dict, which is
|
||||
// part of a Base64 encoded struct. We can Base64 decode to bytes,
|
||||
// swizzle the 4 bytes of the checksum, and then Base64 encode back
|
||||
// to the same number of bytes.
|
||||
/* <dict> */
|
||||
/* <key>blkx</key> */
|
||||
/* <array> */
|
||||
/* <dict> */
|
||||
/* <key>Attributes</key> */
|
||||
/* <string>0x0050</string> */
|
||||
/* <key>CFName</key> */
|
||||
/* <string>Driver Descriptor Map (DDM : 0)</string> */
|
||||
/* <key>Data</key> */
|
||||
/* <data> */
|
||||
/* bWlzaAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAA */
|
||||
/* AAII/////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA */
|
||||
/* AAIAAAAgXDMYCQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA */
|
||||
/* AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA */
|
||||
/* AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA */
|
||||
/* AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA */
|
||||
/* AAAAAAACgAAABgAAAAAAAAAAAAAAAAAAAAAAAAABAAAA */
|
||||
/* AAAAAAAAAAAAAAAANf////8AAAAAAAAAAAAAAAEAAAAA */
|
||||
/* AAAAAAAAAAAAAAA1AAAAAAAAAAA= */
|
||||
/* </data> */
|
||||
/* <key>ID</key> */
|
||||
/* <string>-1</string> */
|
||||
/* <key>Name</key> */
|
||||
/* <string>Driver Descriptor Map (DDM : 0)</string> */
|
||||
/* </dict> */
|
||||
|
||||
/**
|
||||
* Binary representation of data needed to quickly "attribute" a DMG that is
|
||||
* structured for attribution.
|
||||
*/
|
||||
typedef struct AttributionResource {
|
||||
uint32_t signature; /* Set to 'attr'. */
|
||||
uint32_t version; /* Set to 1. */
|
||||
uint32_t beforeCompressedChecksum; /* CRC of compressed bytes before raw block. */
|
||||
uint64_t beforeCompressedLength; /* Number of compressed bytes before raw block. */
|
||||
uint32_t beforeUncompressedChecksum; /* CRC of uncompressed bytes before raw block. */
|
||||
uint64_t beforeUncompressedLength; /* Number of uncompressed bytes before raw block. */
|
||||
uint64_t rawPos; /* Position, in bytes, from start of file to raw block. */
|
||||
uint64_t rawLength; /* Length, in bytes, of raw block. */
|
||||
uint32_t rawChecksum; /* CRC of bytes in raw block. */
|
||||
uint32_t afterCompressedChecksum; /* CRC of compressed bytes after raw block. */
|
||||
uint64_t afterCompressedLength; /* Number of compressed bytes after raw block. */
|
||||
uint32_t afterUncompressedChecksum; /* CRC of uncompressed bytes after raw block. */
|
||||
uint64_t afterUncompressedLength; /* Number of uncompressed bytes after raw block. */
|
||||
} __attribute__ ((packed)) AttributionResource;
|
||||
|
||||
typedef struct AbstractAttribution AbstractAttribution;
|
||||
|
||||
typedef void (*BeforeMainBlkxFunc)(AbstractAttribution* attribution, AbstractFile* abstractOut, ChecksumToken* dataForkToken);
|
||||
typedef int (*ShouldKeepRawFunc)(AbstractAttribution* attribution, const void* data, size_t len);
|
||||
typedef void (*ObserveBuffersFunc)(AbstractAttribution* attribution, int didKeepRaw, const void* uncompressedData, size_t uncompressedLen, const void* compressedData, size_t compressedLen);
|
||||
typedef void (*AfterMainBlkxFunc)(AbstractAttribution* attribution, AbstractFile* abstractOut, ChecksumToken* dataForkToken, AttributionResource* attributionResource);
|
||||
|
||||
struct AbstractAttribution {
|
||||
// Use this to persist state during operation.
|
||||
void* data;
|
||||
|
||||
// Return non-zero if the given buffer should be a raw block (type `BLOCK_RAW`).
|
||||
ShouldKeepRawFunc shouldKeepRaw;
|
||||
// Invoked for each BLKX run with the uncompressed and compressed data.
|
||||
ObserveBuffersFunc observeBuffers;
|
||||
// Invoked once immediately before the main BLKX is inserted.
|
||||
BeforeMainBlkxFunc beforeMainBlkx;
|
||||
// Invoked once immediately after the main BLKX is inserted.
|
||||
AfterMainBlkxFunc afterMainBlkx;
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
// Return an `AbstractAttribution` structure (instance) that will preserve the
|
||||
// *unique* instance of the given string sentinel. If no instance, or more
|
||||
// than one instance, of the given sentinel is found, attribution will fail.
|
||||
AbstractAttribution* createAbstractAttributionPreservingSentinel(const char* sentinel);
|
||||
|
||||
// Copy an "attributable" DMG -- one structured for attribution -- to the
|
||||
// given output, write `len` of the given `bytes` over the `sentinel`.
|
||||
int updateAttribution(AbstractFile* abstractIn, AbstractFile* abstractOut, const char* sentinel, const char* bytes, size_t len);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
#include <hfs/hfsplus.h>
|
||||
#include "abstractfile.h"
|
||||
#include "attribution.h"
|
||||
#include "common.h"
|
||||
|
||||
#define CHECKSUM_UDIF_CRC32 0x00000002
|
||||
|
@ -49,6 +50,10 @@
|
|||
#define BOOTCODE_DMMY 0x444D4D59
|
||||
#define BOOTCODE_GOON 0x676F6F6E
|
||||
|
||||
#define bool short
|
||||
#define true 1
|
||||
#define false 0
|
||||
|
||||
enum {
|
||||
kUDIFFlagsFlattened = 1
|
||||
};
|
||||
|
@ -292,8 +297,8 @@ extern "C" {
|
|||
void readUDIFResourceFile(AbstractFile* file, UDIFResourceFile* o);
|
||||
void writeUDIFResourceFile(AbstractFile* file, UDIFResourceFile* o);
|
||||
|
||||
ResourceKey* readResources(char* xml, size_t length);
|
||||
void writeResources(AbstractFile* file, ResourceKey* resources);
|
||||
ResourceKey* readResources(char* xml, size_t length, bool plstNameIsAttribution);
|
||||
void writeResources(AbstractFile* file, ResourceKey* resources, bool plstNameIsAttribution);
|
||||
void releaseResources(ResourceKey* resources);
|
||||
|
||||
NSizResource* readNSiz(ResourceKey* resources);
|
||||
|
@ -305,8 +310,8 @@ extern "C" {
|
|||
|
||||
ResourceKey* getResourceByKey(ResourceKey* resources, const char* key);
|
||||
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* insertData(ResourceKey* resources, const char* key, int id, const char* name, size_t nameLength, bool nameAsData, const char* data, size_t dataLength, uint32_t attributes);
|
||||
ResourceKey* makePlst(const char* name, size_t nameLength, bool nameAsData);
|
||||
ResourceKey* makeSize(HFSPlusVolumeHeader* volumeHeader);
|
||||
|
||||
void flipDriverDescriptorRecord(DriverDescriptorRecord* record, char out);
|
||||
|
@ -325,11 +330,11 @@ extern "C" {
|
|||
void extractBLKX(AbstractFile* in, AbstractFile* out, BLKXTable* blkx);
|
||||
BLKXTable* insertBLKX(AbstractFile* out, AbstractFile* in, uint32_t firstSectorNumber, uint32_t numSectors, uint32_t blocksDescriptor,
|
||||
uint32_t checksumType, ChecksumFunc uncompressedChk, void* uncompressedChkToken, ChecksumFunc compressedChk,
|
||||
void* compressedChkToken, Volume* volume, int addComment);
|
||||
void* compressedChkToken, Volume* volume, int addComment, AbstractAttribution* attribution);
|
||||
|
||||
|
||||
int extractDmg(AbstractFile* abstractIn, AbstractFile* abstractOut, int partNum);
|
||||
int buildDmg(AbstractFile* abstractIn, AbstractFile* abstractOut, unsigned int BlockSize);
|
||||
int buildDmg(AbstractFile* abstractIn, AbstractFile* abstractOut, unsigned int BlockSize, const char* sentinel);
|
||||
int convertToISO(AbstractFile* abstractIn, AbstractFile* abstractOut);
|
||||
int convertToDMG(AbstractFile* abstractIn, AbstractFile* abstractOut);
|
||||
#ifdef __cplusplus
|
||||
|
|
|
@ -0,0 +1,501 @@
|
|||
Make sure we have a fresh build:
|
||||
|
||||
$ export BUILDDIR=$TESTDIR/../build
|
||||
$ cd $BUILDDIR
|
||||
$ make 2> /dev/null >/dev/null
|
||||
$ cd $CRAMTMP
|
||||
|
||||
$ $BUILDDIR/dmg/dmg build $TESTDIR/attribution_reference/hdiutila.hfs testa.dmg __MOZILLA__attr-value- >/dev/null
|
||||
|
||||
Note the "attr-value-p" suffix below!
|
||||
|
||||
$ $BUILDDIR/dmg/dmg build $TESTDIR/attribution_reference/hdiutilp.hfs testb.dmg __MOZILLA__attr-value- >/dev/null
|
||||
|
||||
$ xxd testa.dmg > testa.txt
|
||||
$ xxd testb.dmg > testb.txt
|
||||
$ diff --unified=3 testa.txt testb.txt
|
||||
--- testa.txt* (glob)
|
||||
+++ testb.txt* (glob)
|
||||
@@ -3349,7 +3349,7 @@
|
||||
0000d140: 7400 7200 2d00 6b00 6500 7900 0000 1000 t.r.-.k.e.y.....
|
||||
0000d150: 0000 0000 0000 0000 0000 175f 5f4d 4f5a ...........__MOZ
|
||||
0000d160: 494c 4c41 5f5f 6174 7472 2d76 616c 7565 ILLA__attr-value
|
||||
-0000d170: 2d61 0000 0000 0000 0000 0000 0000 0000 -a..............
|
||||
+0000d170: 2d70 0000 0000 0000 0000 0000 0000 0000 -p..............
|
||||
0000d180: 0000 0000 0000 0000 0000 0000 0000 0000 ................
|
||||
0000d190: 0000 0000 0000 0000 0000 0000 0000 0000 ................
|
||||
0000d1a0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
|
||||
@@ -16652,7 +16652,7 @@
|
||||
000410b0: 4141 6741 4141 4141 4141 4141 4141 4141 AAgAAAAAAAAAAAAA
|
||||
000410c0: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA
|
||||
000410d0: 4141 4141 410a 0909 0909 4141 4941 4141 AAAAA.....AAIAAA
|
||||
-000410e0: 4167 3544 5163 6877 4141 4141 4141 4141 Ag5DQchwAAAAAAAA
|
||||
+000410e0: 4167 2b53 4962 7641 4141 4141 4141 4141 Ag+SIbvAAAAAAAAA
|
||||
000410f0: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA
|
||||
00041100: 4141 4141 4141 0a09 0909 0941 4141 4141 AAAAAA.....AAAAA
|
||||
00041110: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA
|
||||
@@ -16831,7 +16831,7 @@
|
||||
00041be0: 3c2f 7374 7269 6e67 3e0a 0909 0909 3c6b </string>.....<k
|
||||
00041bf0: 6579 3e44 6174 613c 2f6b 6579 3e0a 0909 ey>Data</key>...
|
||||
00041c00: 0909 3c64 6174 613e 0a09 0909 0941 5141 ..<data>.....AQA
|
||||
-00041c10: 4341 4141 412f 4d47 4e7a 773d 3d0a 0909 CAAAA/MGNzw==...
|
||||
+00041c10: 4341 4141 412b 3845 4638 413d 3d0a 0909 CAAAA+8EF8A==...
|
||||
00041c20: 0909 3c2f 6461 7461 3e0a 0909 0909 3c6b ..</data>.....<k
|
||||
00041c30: 6579 3e49 443c 2f6b 6579 3e0a 0909 0909 ey>ID</key>.....
|
||||
00041c40: 3c73 7472 696e 673e 323c 2f73 7472 696e <string>2</strin
|
||||
@@ -16952,15 +16952,15 @@
|
||||
00042370: 6c6a 6444 344b 4354 7872 5a58 6b2b 5530 ljdD4KCTxrZXk+U0
|
||||
00042380: 6842 4c54 4574 5a47 6c6e 5a58 4e30 5043 hBLTEtZGlnZXN0PC
|
||||
00042390: 3972 5a58 6b2b 4367 6b38 0a09 0909 095a 9rZXk+Cgk8.....Z
|
||||
-000423a0: 4746 3059 5434 4b43 5546 3253 5564 4253 GF0YT4KCUF2SUdBS
|
||||
-000423b0: 446c 3552 6d78 4e5a 4856 6f63 4374 6b4d Dl5RmxNZHVocCtkM
|
||||
-000423c0: 6b4a 454d 7a6c 5656 6e4a 7a0a 0909 0909 kJEMzlVVnJz.....
|
||||
-000423d0: 5154 304b 4354 7776 5a47 4630 5954 344b QT0KCTwvZGF0YT4K
|
||||
+000423a0: 4746 3059 5434 4b43 564e 6e62 444a 784d GF0YT4KCVNnbDJxM
|
||||
+000423b0: 7a5a 7663 6b64 7659 3164 7564 3052 485a zZvckdvY1dud0RHZ
|
||||
+000423c0: 6b4d 324f 5868 5254 3077 790a 0909 0909 kM2OXhRT0wy.....
|
||||
+000423d0: 5754 304b 4354 7776 5a47 4630 5954 344b WT0KCTwvZGF0YT4K
|
||||
000423e0: 4354 7872 5a58 6b2b 596d 7876 5932 7374 CTxrZXk+YmxvY2st
|
||||
000423f0: 5932 686c 5932 747a 6457 3074 0a09 0909 Y2hlY2tzdW0t....
|
||||
00042400: 094d 6a77 7661 3256 3550 676f 4a50 476c .Mjwva2V5PgoJPGl
|
||||
-00042410: 7564 4756 6e5a 5849 2b4c 5467 784d 6a63 udGVnZXI+LTgxMjc
|
||||
-00042420: 354d 7a4d 304f 4477 7661 5735 300a 0909 5MzM0ODwvaW50...
|
||||
+00042410: 7564 4756 6e5a 5849 2b4c 5449 324f 4441 udGVnZXI+LTI2ODA
|
||||
+00042420: 314f 4445 784e 7a77 7661 5735 300a 0909 1ODExNzwvaW50...
|
||||
00042430: 0909 5a57 646c 636a 344b 4354 7872 5a58 ..ZWdlcj4KCTxrZX
|
||||
00042440: 6b2b 596e 6c30 5a58 4d38 4c32 746c 6554 k+Ynl0ZXM8L2tleT
|
||||
00042450: 344b 4354 7870 626e 526c 5a32 5679 0a09 4KCTxpbnRlZ2Vy..
|
||||
@@ -17105,8 +17105,8 @@
|
||||
00042d00: 4141 4258 3344 424d 4877 4541 4141 4141 AABX3DBMHwEAAAAA
|
||||
00042d10: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA
|
||||
00042d20: 4141 4166 0a09 0909 0941 5141 4141 4141 AAAf.....AQAAAAA
|
||||
-00042d30: 4141 4141 4142 4141 4141 4141 415a 3871 AAAAABAAAAAAAZ8q
|
||||
-00042d40: 3836 3646 494d 3855 7442 5141 4141 4141 866FIM8UtBQAAAAA
|
||||
+00042d30: 4141 4141 4142 4141 4141 4141 4170 7856 AAAAABAAAAAAApxV
|
||||
+00042d40: 6f30 4b46 494d 3855 7442 5141 4141 4141 o0KFIM8UtBQAAAAA
|
||||
00042d50: 4141 4a62 690a 0909 0909 4171 3041 5945 AAJbi.....Aq0AYE
|
||||
00042d60: 7341 4141 4141 4141 3d3d 0a09 0909 093c sAAAAAAA==.....<
|
||||
00042d70: 2f73 7472 696e 673e 0a09 0909 3c2f 6469 /string>....</di
|
||||
@@ -17159,8 +17159,8 @@
|
||||
00043060: 0000 0000 0000 0000 0000 0004 064c 0000 .............L..
|
||||
00043070: 0000 0000 0000 0000 0000 0000 0000 0000 ................
|
||||
00043080: 0001 0000 0001 7348 3366 6998 3c64 c623 ......sH3fi.<d.#
|
||||
-00043090: 7b32 6745 8b6b 0000 0002 0000 0020 8e77 {2gE.k....... .w
|
||||
-000430a0: 24c9 0000 0000 0000 0000 0000 0000 0000 $...............
|
||||
+00043090: 7b32 6745 8b6b 0000 0002 0000 0020 8eb5 {2gE.k....... ..
|
||||
+000430a0: c555 0000 0000 0000 0000 0000 0000 0000 .U..............
|
||||
000430b0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
|
||||
000430c0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
|
||||
000430d0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
|
||||
@@ -17176,8 +17176,8 @@
|
||||
00043170: 0000 0000 0000 0000 0000 0000 0000 0000 ................
|
||||
00043180: 0000 0000 0000 0000 0000 0000 0000 0000 ................
|
||||
00043190: 0000 0000 0000 0000 0000 0000 0000 0000 ................
|
||||
-000431a0: 0000 0000 0000 0000 0002 0000 0020 2339 ............. #9
|
||||
-000431b0: ec78 0000 0000 0000 0000 0000 0000 0000 .x..............
|
||||
+000431a0: 0000 0000 0000 0000 0002 0000 0020 0b7d ............. .}
|
||||
+000431b0: d859 0000 0000 0000 0000 0000 0000 0000 .Y..............
|
||||
000431c0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
|
||||
000431d0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
|
||||
000431e0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
|
||||
[1]
|
||||
|
||||
Check resources:
|
||||
|
||||
$ $BUILDDIR/dmg/dmg res testa.dmg - | expand -t 4
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>resource-fork</key>
|
||||
<dict>
|
||||
<key>blkx</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>Attributes</key>
|
||||
<string>0x0050</string>
|
||||
<key>CFName</key>
|
||||
<string>Driver Descriptor Map (DDM : 0)</string>
|
||||
<key>Data</key>
|
||||
<data>
|
||||
bWlzaAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAA
|
||||
AAII/////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAIAAAAgXDMYCQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAACgAAABgAAAAAAAAAAAAAAAAAAAAAAAAABAAAA
|
||||
AAAAAAAAAAAAAAAANf////8AAAAAAAAAAAAAAAEAAAAA
|
||||
AAAAAAAAAAAAAAA1AAAAAAAAAAA=
|
||||
</data>
|
||||
<key>ID</key>
|
||||
<string>-1</string>
|
||||
<key>Name</key>
|
||||
<string>Driver Descriptor Map (DDM : 0)</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>Attributes</key>
|
||||
<string>0x0050</string>
|
||||
<key>CFName</key>
|
||||
<string>Apple (Apple_partition_map : 1)</string>
|
||||
<key>Data</key>
|
||||
<data>
|
||||
bWlzaAAAAAEAAAAAAAAAAQAAAAAAAAA/AAAAAAAAAAAA
|
||||
AAIIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAIAAAAg7xHa2gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAACgAAABgAAAAAAAAAAAAAAAAAAAAAAAAA/AAAA
|
||||
AAAAADUAAAAAAAAAv/////8AAAAAAAAAAAAAAD8AAAAA
|
||||
AAAAAAAAAAAAAAD0AAAAAAAAAAA=
|
||||
</data>
|
||||
<key>ID</key>
|
||||
<string>0</string>
|
||||
<key>Name</key>
|
||||
<string>Apple (Apple_partition_map : 1)</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>Attributes</key>
|
||||
<string>0x0050</string>
|
||||
<key>CFName</key>
|
||||
<string>Macintosh (Apple_Driver_ATAPI : 2)</string>
|
||||
<key>Data</key>
|
||||
<data>
|
||||
bWlzaAAAAAEAAAAAAAAAQAAAAAAAAAAIAAAAAAAAAAAA
|
||||
AAIIAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAIAAAAgxxwAEQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAACgAAABgAAAAAAAAAAAAAAAAAAAAAAAAAIAAAA
|
||||
AAAAAPQAAAAAAAAAK/////8AAAAAAAAAAAAAAAgAAAAA
|
||||
AAAAAAAAAAAAAAEfAAAAAAAAAAA=
|
||||
</data>
|
||||
<key>ID</key>
|
||||
<string>1</string>
|
||||
<key>Name</key>
|
||||
<string>Macintosh (Apple_Driver_ATAPI : 2)</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>Attributes</key>
|
||||
<string>0x0050</string>
|
||||
<key>CFName</key>
|
||||
<string>Mac_OS_X (Apple_HFSX : 3)</string>
|
||||
<key>Data</key>
|
||||
<data>
|
||||
bWlzaAAAAAEAAAAAAAAASAAAAAAAACewAAAAAAAAAAAA
|
||||
AAIIAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAIAAAAg5DQchwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAVAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAIAAAAA
|
||||
AAAAAR8AAAAAAAQAAIAAAAYAAAAAAAAAAAAAAgAAAAAA
|
||||
AAACAAAAAAAABAEfAAAAAAAAAC2AAAAGAAAAAAAAAAAA
|
||||
AAQAAAAAAAAAAgAAAAAAAAQBTAAAAAAAAAF0gAAABgAA
|
||||
AAAAAAAAAAAGAAAAAAAAAAIAAAAAAAAEAsAAAAAAAAAA
|
||||
LYAAAAYAAAAAAAAAAAAACAAAAAAAAAACAAAAAAAABALt
|
||||
AAAAAAAAAEiAAAAGAAAAAAAAAAAAAAoAAAAAAAAAAgAA
|
||||
AAAAAAQDNQAAAAAAAAAtgAAABgAAAAAAAAAAAAAMAAAA
|
||||
AAAAAAIAAAAAAAAEA2IAAAAAAAAALYAAAAYAAAAAAAAA
|
||||
AAAADgAAAAAAAAACAAAAAAAABAOPAAAAAAAAAC2AAAAG
|
||||
AAAAAAAAAAAAABAAAAAAAAAAAgAAAAAAAAQDvAAAAAAA
|
||||
AAAtgAAABgAAAAAAAAAAAAASAAAAAAAAAAIAAAAAAAAE
|
||||
A+kAAAAAAAAALYAAAAYAAAAAAAAAAAAAFAAAAAAAAAAC
|
||||
AAAAAAAABAQWAAAAAAAAAC2AAAAGAAAAAAAAAAAAABYA
|
||||
AAAAAAAAAgAAAAAAAAQEQwAAAAAAAAAtgAAABgAAAAAA
|
||||
AAAAAAAYAAAAAAAAAAIAAAAAAAAEBHAAAAAAAAAALYAA
|
||||
AAYAAAAAAAAAAAAAGgAAAAAAAAACAAAAAAAABASdAAAA
|
||||
AAAAAC2AAAAGAAAAAAAAAAAAABwAAAAAAAAAAgAAAAAA
|
||||
AAQEygAAAAAAAAAtgAAABgAAAAAAAAAAAAAeAAAAAAAA
|
||||
AAIAAAAAAAAEBPcAAAAAAAAALYAAAAYAAAAAAAAAAAAA
|
||||
IAAAAAAAAAACAAAAAAAABAUkAAAAAAAAAC2AAAAGAAAA
|
||||
AAAAAAAAACIAAAAAAAAAAgAAAAAAAAQFUQAAAAAAAAAt
|
||||
gAAABgAAAAAAAAAAAAAkAAAAAAAAAAIAAAAAAAAEBX4A
|
||||
AAAAAAAALYAAAAYAAAAAAAAAAAAAJgAAAAAAAAABsAAA
|
||||
AAAABAWrAAAAAAAAAKH/////AAAAAAAAAAAAACewAAAA
|
||||
AAAAAAAAAAAAAAQGTAAAAAAAAAAA
|
||||
</data>
|
||||
<key>ID</key>
|
||||
<string>2</string>
|
||||
<key>Name</key>
|
||||
<string>Mac_OS_X (Apple_HFSX : 3)</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>Attributes</key>
|
||||
<string>0x0050</string>
|
||||
<key>CFName</key>
|
||||
<string> (Apple_Free : 4)</string>
|
||||
<key>Data</key>
|
||||
<data>
|
||||
bWlzaAAAAAEAAAAAAAAn+AAAAAAAAAAKAAAAAAAAAAAA
|
||||
AAAAAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAIAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAACAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAKAAAA
|
||||
AAAEBkwAAAAAAAAAAP////8AAAAAAAAAAAAAAAoAAAAA
|
||||
AAAAAAAAAAAABAZMAAAAAAAAAAA=
|
||||
</data>
|
||||
<key>ID</key>
|
||||
<string>3</string>
|
||||
<key>Name</key>
|
||||
<string> (Apple_Free : 4)</string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>cSum</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>Attributes</key>
|
||||
<string>0x0000</string>
|
||||
<key>Data</key>
|
||||
<data>
|
||||
AQACAAAAhY246A==
|
||||
</data>
|
||||
<key>ID</key>
|
||||
<string>0</string>
|
||||
<key>Name</key>
|
||||
<string></string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>Attributes</key>
|
||||
<string>0x0000</string>
|
||||
<key>Data</key>
|
||||
<data>
|
||||
AQACAAAAAAAAAA==
|
||||
</data>
|
||||
<key>ID</key>
|
||||
<string>1</string>
|
||||
<key>Name</key>
|
||||
<string></string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>Attributes</key>
|
||||
<string>0x0000</string>
|
||||
<key>Data</key>
|
||||
<data>
|
||||
AQACAAAA/MGNzw==
|
||||
</data>
|
||||
<key>ID</key>
|
||||
<string>2</string>
|
||||
<key>Name</key>
|
||||
<string></string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>nsiz</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>Attributes</key>
|
||||
<string>0x0000</string>
|
||||
<key>Data</key>
|
||||
<data>
|
||||
PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRG
|
||||
LTgiPz4KPCFET0NUWVBFIHBsaXN0IFBVQkxJQyAiLS8v
|
||||
QXBwbGUvL0RURCBQTElTVCAxLjAvL0VOIiAiaHR0cDov
|
||||
L3d3dy5hcHBsZS5jb20vRFREcy9Qcm9wZXJ0eUxpc3Qt
|
||||
MS4wLmR0ZCI+CjxwbGlzdCB2ZXJzaW9uPSIxLjAiPgo8
|
||||
ZGljdD4KCTxrZXk+YmxvY2stY2hlY2tzdW0tMjwva2V5
|
||||
PgoJPGludGVnZXI+LTM5MDU1ODMzMTwvaW50ZWdlcj4K
|
||||
CTxrZXk+cGFydC1udW08L2tleT4KCTxpbnRlZ2VyPjA8
|
||||
L2ludGVnZXI+Cgk8a2V5PnZlcnNpb248L2tleT4KCTxp
|
||||
bnRlZ2VyPjY8L2ludGVnZXI+CjwvZGljdD4KPC9wbGlz
|
||||
dD4K
|
||||
</data>
|
||||
<key>ID</key>
|
||||
<string>0</string>
|
||||
<key>Name</key>
|
||||
<string></string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>Attributes</key>
|
||||
<string>0x0000</string>
|
||||
<key>Data</key>
|
||||
<data>
|
||||
PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRG
|
||||
LTgiPz4KPCFET0NUWVBFIHBsaXN0IFBVQkxJQyAiLS8v
|
||||
QXBwbGUvL0RURCBQTElTVCAxLjAvL0VOIiAiaHR0cDov
|
||||
L3d3dy5hcHBsZS5jb20vRFREcy9Qcm9wZXJ0eUxpc3Qt
|
||||
MS4wLmR0ZCI+CjxwbGlzdCB2ZXJzaW9uPSIxLjAiPgo8
|
||||
ZGljdD4KCTxrZXk+YmxvY2stY2hlY2tzdW0tMjwva2V5
|
||||
PgoJPGludGVnZXI+MDwvaW50ZWdlcj4KCTxrZXk+cGFy
|
||||
dC1udW08L2tleT4KCTxpbnRlZ2VyPjE8L2ludGVnZXI+
|
||||
Cgk8a2V5PnZlcnNpb248L2tleT4KCTxpbnRlZ2VyPjY8
|
||||
L2ludGVnZXI+CjwvZGljdD4KPC9wbGlzdD4K
|
||||
</data>
|
||||
<key>ID</key>
|
||||
<string>1</string>
|
||||
<key>Name</key>
|
||||
<string></string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>Attributes</key>
|
||||
<string>0x0000</string>
|
||||
<key>Data</key>
|
||||
<data>
|
||||
PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRG
|
||||
LTgiPz4KPCFET0NUWVBFIHBsaXN0IFBVQkxJQyAiLS8v
|
||||
QXBwbGUvL0RURCBQTElTVCAxLjAvL0VOIiAiaHR0cDov
|
||||
L3d3dy5hcHBsZS5jb20vRFREcy9Qcm9wZXJ0eUxpc3Qt
|
||||
MS4wLmR0ZCI+CjxwbGlzdCB2ZXJzaW9uPSIxLjAiPgo8
|
||||
ZGljdD4KCTxrZXk+U0hBLTEtZGlnZXN0PC9rZXk+Cgk8
|
||||
ZGF0YT4KCUF2SUdBSDl5RmxNZHVocCtkMkJEMzlVVnJz
|
||||
QT0KCTwvZGF0YT4KCTxrZXk+YmxvY2stY2hlY2tzdW0t
|
||||
Mjwva2V5PgoJPGludGVnZXI+LTgxMjc5MzM0ODwvaW50
|
||||
ZWdlcj4KCTxrZXk+Ynl0ZXM8L2tleT4KCTxpbnRlZ2Vy
|
||||
PjE1NTY0ODwvaW50ZWdlcj4KCTxrZXk+ZGF0ZTwva2V5
|
||||
PgoJPGludGVnZXI+LTUzMjU5Nzk4OTwvaW50ZWdlcj4K
|
||||
CTxrZXk+cGFydC1udW08L2tleT4KCTxpbnRlZ2VyPjI8
|
||||
L2ludGVnZXI+Cgk8a2V5PnZlcnNpb248L2tleT4KCTxp
|
||||
bnRlZ2VyPjY8L2ludGVnZXI+Cgk8a2V5PnZvbHVtZS1z
|
||||
aWduYXR1cmU8L2tleT4KCTxpbnRlZ2VyPjE4NDc1PC9p
|
||||
bnRlZ2VyPgo8L2RpY3Q+CjwvcGxpc3Q+Cg==
|
||||
</data>
|
||||
<key>ID</key>
|
||||
<string>2</string>
|
||||
<key>Name</key>
|
||||
<string></string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>plst</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>Attributes</key>
|
||||
<string>0x0050</string>
|
||||
<key>Data</key>
|
||||
<data>
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAQAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAA
|
||||
</data>
|
||||
<key>ID</key>
|
||||
<string>0</string>
|
||||
<key>Name</key>
|
||||
<string>
|
||||
cnR0YQEAAABX3DBMHwEAAAAAAAAAAAAAAAAAAAAAAAAf
|
||||
AQAAAAAAAAAABAAAAAAAZ8q866FIM8UtBQAAAAAAAJbi
|
||||
Aq0AYEsAAAAAAA==
|
||||
</string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>size</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>Attributes</key>
|
||||
<string>0x0000</string>
|
||||
<key>Data</key>
|
||||
<data>
|
||||
BQABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAABszQeAAAAAAK0gBAA==
|
||||
</data>
|
||||
<key>ID</key>
|
||||
<string>2</string>
|
||||
<key>Name</key>
|
||||
<string></string>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
Line in the sand:
|
||||
|
||||
$ shasum testa.dmg testb.dmg
|
||||
85f7e85acef53b63fc199e84ff801ae96b3e8c83 testa.dmg
|
||||
ef6e91b330f5eacd10eb5e37603b8baa06d4a1a4 testb.dmg
|
||||
|
||||
Attribute:
|
||||
|
||||
$ $BUILDDIR/dmg/dmg attribute testa.dmg testa_updated.dmg __MOZILLA__attr-value- __MOZILLA__attr-value-p >/dev/null
|
||||
|
||||
Unfortunately, attributing builds does not update the cSum block, so we expect some differences here:
|
||||
|
||||
$ xxd testa_updated.dmg > testa_updated.txt
|
||||
$ diff --unified=3 testb.txt testa_updated.txt
|
||||
--- testb.txt* (glob)
|
||||
+++ testa_updated.txt* (glob)
|
||||
@@ -16831,7 +16831,7 @@
|
||||
00041be0: 3c2f 7374 7269 6e67 3e0a 0909 0909 3c6b </string>.....<k
|
||||
00041bf0: 6579 3e44 6174 613c 2f6b 6579 3e0a 0909 ey>Data</key>...
|
||||
00041c00: 0909 3c64 6174 613e 0a09 0909 0941 5141 ..<data>.....AQA
|
||||
-00041c10: 4341 4141 412b 3845 4638 413d 3d0a 0909 CAAAA+8EF8A==...
|
||||
+00041c10: 4341 4141 412f 4d47 4e7a 773d 3d0a 0909 CAAAA/MGNzw==...
|
||||
00041c20: 0909 3c2f 6461 7461 3e0a 0909 0909 3c6b ..</data>.....<k
|
||||
00041c30: 6579 3e49 443c 2f6b 6579 3e0a 0909 0909 ey>ID</key>.....
|
||||
00041c40: 3c73 7472 696e 673e 323c 2f73 7472 696e <string>2</strin
|
||||
@@ -16952,15 +16952,15 @@
|
||||
00042370: 6c6a 6444 344b 4354 7872 5a58 6b2b 5530 ljdD4KCTxrZXk+U0
|
||||
00042380: 6842 4c54 4574 5a47 6c6e 5a58 4e30 5043 hBLTEtZGlnZXN0PC
|
||||
00042390: 3972 5a58 6b2b 4367 6b38 0a09 0909 095a 9rZXk+Cgk8.....Z
|
||||
-000423a0: 4746 3059 5434 4b43 564e 6e62 444a 784d GF0YT4KCVNnbDJxM
|
||||
-000423b0: 7a5a 7663 6b64 7659 3164 7564 3052 485a zZvckdvY1dud0RHZ
|
||||
-000423c0: 6b4d 324f 5868 5254 3077 790a 0909 0909 kM2OXhRT0wy.....
|
||||
-000423d0: 5754 304b 4354 7776 5a47 4630 5954 344b WT0KCTwvZGF0YT4K
|
||||
+000423a0: 4746 3059 5434 4b43 5546 3253 5564 4253 GF0YT4KCUF2SUdBS
|
||||
+000423b0: 446c 3552 6d78 4e5a 4856 6f63 4374 6b4d Dl5RmxNZHVocCtkM
|
||||
+000423c0: 6b4a 454d 7a6c 5656 6e4a 7a0a 0909 0909 kJEMzlVVnJz.....
|
||||
+000423d0: 5154 304b 4354 7776 5a47 4630 5954 344b QT0KCTwvZGF0YT4K
|
||||
000423e0: 4354 7872 5a58 6b2b 596d 7876 5932 7374 CTxrZXk+YmxvY2st
|
||||
000423f0: 5932 686c 5932 747a 6457 3074 0a09 0909 Y2hlY2tzdW0t....
|
||||
00042400: 094d 6a77 7661 3256 3550 676f 4a50 476c .Mjwva2V5PgoJPGl
|
||||
-00042410: 7564 4756 6e5a 5849 2b4c 5449 324f 4441 udGVnZXI+LTI2ODA
|
||||
-00042420: 314f 4445 784e 7a77 7661 5735 300a 0909 1ODExNzwvaW50...
|
||||
+00042410: 7564 4756 6e5a 5849 2b4c 5467 784d 6a63 udGVnZXI+LTgxMjc
|
||||
+00042420: 354d 7a4d 304f 4477 7661 5735 300a 0909 5MzM0ODwvaW50...
|
||||
00042430: 0909 5a57 646c 636a 344b 4354 7872 5a58 ..ZWdlcj4KCTxrZX
|
||||
00042440: 6b2b 596e 6c30 5a58 4d38 4c32 746c 6554 k+Ynl0ZXM8L2tleT
|
||||
00042450: 344b 4354 7870 626e 526c 5a32 5679 0a09 4KCTxpbnRlZ2Vy..
|
||||
[1]
|
||||
$ shasum testb.dmg testa_updated.dmg
|
||||
ef6e91b330f5eacd10eb5e37603b8baa06d4a1a4 testb.dmg
|
||||
41fadf80df5625ac458a0c1c548f37afbc41b06e testa_updated.dmg
|
||||
|
||||
However, if we revert, the checksums should match again:
|
||||
We could also revert:
|
||||
|
||||
$ $BUILDDIR/dmg/dmg attribute testa_updated.dmg testa_reverted.dmg __MOZILLA__attr-value- __MOZILLA__attr-value-a >/dev/null
|
||||
|
||||
Note -- same same:
|
||||
|
||||
$ xxd testa_reverted.dmg > testa_reverted.txt
|
||||
$ diff --unified=3 testa.txt testa_reverted.txt
|
||||
$ shasum testa.dmg testa_reverted.dmg
|
||||
85f7e85acef53b63fc199e84ff801ae96b3e8c83 testa.dmg
|
||||
85f7e85acef53b63fc199e84ff801ae96b3e8c83 testa_reverted.dmg
|
|
@ -0,0 +1,45 @@
|
|||
This takes about 10 seconds, which is too slow for constant inputs.
|
||||
Run this like
|
||||
```
|
||||
time venv/bin/cram test/attribution.t --keep-tmpdir
|
||||
```
|
||||
and then copy the reference directory like
|
||||
```
|
||||
cp -R /var/folders/3s/_m9prk6n7g5cx6hhs_33q2f80000gn/T/cramtests-0uzbp0wu/reference test/reference
|
||||
```
|
||||
to update test inputs.
|
||||
|
||||
Make sure we have a fresh build:
|
||||
|
||||
$ export BUILDDIR=$TESTDIR/../build
|
||||
$ cd $BUILDDIR
|
||||
$ make &> /dev/null
|
||||
$ cd $CRAMTMP
|
||||
|
||||
Prepare content:
|
||||
|
||||
$ mkdir stagedir
|
||||
$ echo "content-x" >> stagedir/x
|
||||
|
||||
Create reference DMGs using macOS:
|
||||
|
||||
$ mkdir reference
|
||||
$ xattr -w 'attr-key' '__MOZILLA__attr-value-a' stagedir/x
|
||||
$ hdiutil create -megabytes 5 -fs HFS+ -volname myDisk -srcfolder stagedir reference/hdiutila.hfs
|
||||
created: */reference/hdiutila.hfs.dmg (glob)
|
||||
$ xattr -w 'attr-key' '__MOZILLA__attr-value-b' stagedir/x
|
||||
$ hdiutil create -megabytes 5 -fs HFS+ -volname myDisk -srcfolder stagedir reference/hdiutilb.hfs
|
||||
created: */reference/hdiutilb.hfs.dmg (glob)
|
||||
$ xattr -w 'attr-key' '__MOZILLA__attr-value-p' stagedir/x
|
||||
$ hdiutil create -megabytes 5 -fs HFS+ -volname myDisk -srcfolder stagedir reference/hdiutilp.hfs
|
||||
created: */reference/hdiutilp.hfs.dmg (glob)
|
||||
|
||||
Extract reference HFSs:
|
||||
|
||||
$ $BUILDDIR/dmg/dmg extract reference/hdiutila.hfs.dmg reference/hdiutila.hfs > /dev/null
|
||||
$ $BUILDDIR/dmg/dmg extract reference/hdiutilb.hfs.dmg reference/hdiutilb.hfs > /dev/null
|
||||
$ $BUILDDIR/dmg/dmg extract reference/hdiutilp.hfs.dmg reference/hdiutilp.hfs > /dev/null
|
||||
|
||||
Remove the unneeded dmg:
|
||||
|
||||
$ rm reference/hdiutilp.hfs.dmg
|
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
|
@ -16,17 +16,20 @@ Create reference DMGs using macOS:
|
|||
$ mkdir reference
|
||||
$ hdiutil create -megabytes 5 -fs HFS+ -volname myDisk -srcfolder stagedir reference/hdiutil.hfs
|
||||
created: */reference/hdiutil.hfs.dmg (glob)
|
||||
$ xattr -w 'attr-key-a' 'attr-value-a' stagedir/a
|
||||
$ xattr -w 'attr-key-a' '__MOZILLA__attr-value-a' stagedir/a
|
||||
$ hdiutil create -megabytes 5 -fs HFS+ -volname myDisk -srcfolder stagedir reference/hdiutila.hfs
|
||||
created: */reference/hdiutila.hfs.dmg (glob)
|
||||
$ xattr -w 'attr-key-b' 'attr-value-b' stagedir/b
|
||||
$ xattr -w 'attr-key-b' '__MOZILLA__attr-value-b' stagedir/b
|
||||
$ hdiutil create -megabytes 5 -fs HFS+ -volname myDisk -srcfolder stagedir reference/hdiutilab.hfs
|
||||
created: */reference/hdiutilab.hfs.dmg (glob)
|
||||
$ xattr -c stagedir/a
|
||||
$ hdiutil create -megabytes 5 -fs HFS+ -volname myDisk -srcfolder stagedir reference/hdiutilb.hfs
|
||||
|
||||
Extract reference HFSs:
|
||||
|
||||
$ $BUILDDIR/dmg/dmg extract reference/hdiutil.hfs.dmg reference/hdiutil.hfs > /dev/null
|
||||
$ $BUILDDIR/dmg/dmg extract reference/hdiutila.hfs.dmg reference/hdiutila.hfs > /dev/null
|
||||
$ $BUILDDIR/dmg/dmg extract reference/hdiutilb.hfs.dmg reference/hdiutilb.hfs > /dev/null
|
||||
$ $BUILDDIR/dmg/dmg extract reference/hdiutilab.hfs.dmg reference/hdiutilab.hfs > /dev/null
|
||||
|
||||
Generate comparison HFSs:
|
||||
|
@ -38,12 +41,26 @@ Generate comparison HFSs:
|
|||
Setting permissions to 100644 for /a
|
||||
file: /b
|
||||
Setting permissions to 100644 for /b
|
||||
$ $BUILDDIR/hfs/hfsplus output/stageda.hfs setattr a 'attr-key-a' 'attr-value-a'
|
||||
$ $BUILDDIR/hfs/hfsplus output/stageda.hfs setattr a 'attr-key-a' '__MOZILLA__attr-value-a__MOZILLA__'
|
||||
$ cp $TESTDIR/empty.hfs output/stagedap.hfs
|
||||
$ $BUILDDIR/hfs/hfsplus output/stagedap.hfs addall stagedir
|
||||
file: /a
|
||||
Setting permissions to 100644 for /a
|
||||
file: /b
|
||||
Setting permissions to 100644 for /b
|
||||
$ $BUILDDIR/hfs/hfsplus output/stagedap.hfs setattr a 'attr-key-a' '__MOZILLA__attr-value-p__MOZILLA__'
|
||||
$ cp $TESTDIR/empty.hfs output/stagedab.hfs
|
||||
$ $BUILDDIR/hfs/hfsplus output/stagedab.hfs addall stagedir
|
||||
file: /a
|
||||
Setting permissions to 100644 for /a
|
||||
file: /b
|
||||
Setting permissions to 100644 for /b
|
||||
$ $BUILDDIR/hfs/hfsplus output/stagedab.hfs setattr a 'attr-key-a' 'attr-value-a'
|
||||
$ $BUILDDIR/hfs/hfsplus output/stagedab.hfs setattr b 'attr-key-b' 'attr-value-b'
|
||||
$ $BUILDDIR/hfs/hfsplus output/stagedab.hfs setattr a 'attr-key-a' '__MOZILLA__attr-value-a__MOZILLA__'
|
||||
$ $BUILDDIR/hfs/hfsplus output/stagedab.hfs setattr b 'attr-key-b' '__MOZILLA__attr-value-b__MOZILLA__'
|
||||
$ cp $TESTDIR/empty.hfs output/stagedb.hfs
|
||||
$ $BUILDDIR/hfs/hfsplus output/stagedb.hfs addall stagedir
|
||||
file: /a
|
||||
Setting permissions to 100644 for /a
|
||||
file: /b
|
||||
Setting permissions to 100644 for /b
|
||||
$ $BUILDDIR/hfs/hfsplus output/stagedb.hfs setattr b 'attr-key-b' '__MOZILLA__attr-value-b__MOZILLA__'
|
||||
|
|
Загрузка…
Ссылка в новой задаче