[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:
Nick Alexander 2023-03-22 15:19:56 -07:00 коммит произвёл Ben Hearsum
Родитель 89b98b91b9
Коммит 5961355107
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: A4B76C1E8BED5BFD
21 изменённых файлов: 1240 добавлений и 63 удалений

2
.gitignore поставляемый
Просмотреть файл

@ -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)

327
dmg/attribution.c Normal file
Просмотреть файл

@ -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;
}

Просмотреть файл

@ -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,16 +72,21 @@ 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;

Просмотреть файл

@ -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);

Просмотреть файл

@ -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;

Просмотреть файл

@ -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,7 +390,16 @@ static void readResourceData(ResourceData* data, char** location, char* xmlStart
sscanf(buffer, "%d", &(data->id));
free(buffer);
} else if(strncmp(tagBegin, "Name", strLen) == 0) {
data->name = getXMLString(&curLoc);
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);
}
}
}
@ -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);
abstractFilePrint(file, "%s\t<key>Name</key>\n%s\t<string>%s</string>\n", tabs, tabs, data->name);
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,11 +846,11 @@ 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;
lastResource = resources;
curResource = resources;
while(curResource != NULL) {
@ -836,8 +913,14 @@ ResourceKey* insertData(ResourceKey* resources, const char* key, int id, const c
curData->attributes = attributes;
curData->dataLength = dataLength;
curData->id = id;
curData->name = (char*) malloc(strlen(name) + 1);
strcpy((char*) curData->name, name);
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);
}

37
docs/attribution.md Normal file
Просмотреть файл

@ -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);

115
includes/dmg/attribution.h Normal file
Просмотреть файл

@ -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

501
test/attribution.t Normal file
Просмотреть файл

@ -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

Двоичные данные
test/attribution_reference/hdiutila.hfs Normal file

Двоичный файл не отображается.

Двоичные данные
test/attribution_reference/hdiutila.hfs.dmg Normal file

Двоичный файл не отображается.

Двоичные данные
test/attribution_reference/hdiutilb.hfs Normal file

Двоичный файл не отображается.

Двоичные данные
test/attribution_reference/hdiutilb.hfs.dmg Normal file

Двоичный файл не отображается.

Двоичные данные
test/attribution_reference/hdiutilp.hfs Normal file

Двоичный файл не отображается.

Просмотреть файл

@ -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__'