diff --git a/third_party/libdmg-hfsplus/src/common/sizedbuf.c b/third_party/libdmg-hfsplus/src/common/sizedbuf.c index 6d6399cbbc17c..0fc3fff616853 100644 --- a/third_party/libdmg-hfsplus/src/common/sizedbuf.c +++ b/third_party/libdmg-hfsplus/src/common/sizedbuf.c @@ -1,6 +1,7 @@ #include "sizedbuf.h" #include +#include #include #include @@ -19,7 +20,7 @@ SizedBuf* AllocBufCopy(const SizedBuf* buf) { return AllocBufCopyBytes(buf->data, buf->len); } -SizedBuf* AllocBufCopyBytes(const char* data, size_t len) { +SizedBuf* AllocBufCopyBytes(const uint8_t* data, size_t len) { ASSERT(data, "AllocBufCopyBytes can't copy out of NULL!"); SizedBuf* ret = malloc(sizeof(SizedBuf) + len); ASSERT(ret, "AllocBufCopyBytes OOM"); @@ -30,7 +31,22 @@ SizedBuf* AllocBufCopyBytes(const char* data, size_t len) { SizedBuf* AllocBufCopyString(const char* str) { ASSERT(str, "AllocBufCopyString can't copy out of NULL!"); - SizedBuf* ret = AllocBufCopyBytes(str, strlen(str) + 1); + SizedBuf* ret = AllocBufCopyBytes((const uint8_t*)str, strlen(str) + 1); ret->len -= 1; return ret; } + +SizedBuf* ReallocBuf(SizedBuf* buf, size_t new_cap) { + if (!buf) { + SizedBuf* ret = ZAllocBuf(new_cap); + ret->len = 0; + return ret; + } + SizedBuf* ret = realloc(buf, sizeof(SizedBuf) + new_cap); + ASSERT(ret, "ReallocBuf failed"); + ret->cap = new_cap; + if (ret->len > new_cap) { + ret->len = new_cap; + } + return ret; +} diff --git a/third_party/libdmg-hfsplus/src/dmg/attribution.c b/third_party/libdmg-hfsplus/src/dmg/attribution.c index a6f5fac18ea79..a51884293deaa 100644 --- a/third_party/libdmg-hfsplus/src/dmg/attribution.c +++ b/third_party/libdmg-hfsplus/src/dmg/attribution.c @@ -1,4 +1,5 @@ #include +#include #include #include // For crc32_combine. #include "abstractfile.h" @@ -30,7 +31,7 @@ typedef struct AttributionPreservingSentinelData { enum ShouldKeepRaw sentinelShouldKeepRaw(AbstractAttribution* attribution, const void* data, size_t len, const void* nextData, size_t nextLen) { AttributionPreservingSentinelData* attributionData = (AttributionPreservingSentinelData*)attribution->data; const SizedBuf* sentinelBuf = attributionData->sentinelBuf; - if (NULL != memmem((const char*)data, len, sentinelBuf->data, sentinelBuf->len)) { + if (NULL != memmem((const uint8_t*)data, len, sentinelBuf->data, sentinelBuf->len)) { return KeepCurrentRaw; } @@ -39,7 +40,7 @@ enum ShouldKeepRaw sentinelShouldKeepRaw(AbstractAttribution* attribution, const char* combinedData = malloc(len + nextLen); memcpy(combinedData, data, len); memcpy(combinedData + len, nextData, nextLen); - if (NULL != memmem((const char*)combinedData, len + nextLen, sentinelBuf->data, sentinelBuf->len)) { + if (NULL != memmem((const uint8_t*)combinedData, len + nextLen, sentinelBuf->data, sentinelBuf->len)) { return KeepCurrentAndNextRaw; } } @@ -197,7 +198,7 @@ uint32_t calculateMasterChecksum(ResourceKey* resources); int updateAttribution(AbstractFile* abstractIn, AbstractFile* abstractOut, const char* anchor, const char* data, size_t dataLen) { SizedBuf* sentinelBuf = AllocBufCopyString(anchor); - SizedBuf* dataBuf = AllocBufCopyBytes(data, dataLen); + SizedBuf* dataBuf = AllocBufCopyBytes((const uint8_t*)data, dataLen); int ret = updateAttributionFromBufs(abstractIn, abstractOut, sentinelBuf, dataBuf); free(dataBuf); free(sentinelBuf); diff --git a/third_party/libdmg-hfsplus/src/hfs/hfs.c b/third_party/libdmg-hfsplus/src/hfs/hfs.c index 680e7bb35910f..4da9fab6b3304 100644 --- a/third_party/libdmg-hfsplus/src/hfs/hfs.c +++ b/third_party/libdmg-hfsplus/src/hfs/hfs.c @@ -11,6 +11,8 @@ #include "hfs/hfslib.h" #include "hfs/hfsplus.h" #include "abstractfile.h" +#include "parse_data_param.h" +#include "sizedbuf.h" #include char endianness; @@ -250,7 +252,7 @@ void cmd_getattr(Volume* volume, int argc, char *argv[]) { free(record); } -void cmd_setattr(Volume* volume, int argc, char *argv[]) { +void cmd_setattr(Volume* volume, DataParamParserPtr data_param_parser, int argc, char *argv[]) { HFSPlusCatalogRecord* record; if (argc < 4) { @@ -271,23 +273,30 @@ void cmd_setattr(Volume* volume, int argc, char *argv[]) { id = folderRecord -> folderID; } - // Note: this doesn't handle embedded nulls, string encodings, etc. - size_t dataLen = strlen(argv[3]); - if (dataLen == 0) { - // Handle the empty string gracefully. - dataLen = 1; + SizedBuf* value = data_param_parser(argv[3]); + // Write empty data as two zero-value bytes instead. This produces an + // empty string if string decoding is intended, then rounds up to an even + // record size. + if (value->len == 0) { + free(value); + value = ZAllocBuf(2); } - if ((dataLen & 0x1) == 0x1) { + + if ((value->len & 0x1) == 0x1) { // HFS record sizes must be even. Pad the given data with one 0 to // maintain this invariant. Note that macOS `xattr` appears to do // this silently. - dataLen += 1; + ASSERT(value->cap >= value->len, "SizedBuf cap below len is bad news"); + if (value->cap == value->len) { + // No space for the 0. Realloc. + value = ReallocBuf(value, value->len + 1); + } + // There is (now) definitely room to extend by one byte. + value->data[value->len] = 0; + value->len += 1; } - uint8_t* data = malloc(sizeof(uint8_t) * (dataLen)); - memset(data, 0, dataLen); - memcpy(data, argv[3], strlen(argv[3])); - ASSERT(setAttribute(volume, id, argv[2], data, dataLen), "setAttribute"); + ASSERT(setAttribute(volume, id, argv[2], value->data, value->len), "setAttribute"); if (fileRecord != NULL) { fileRecord -> flags |= kHFSHasAttributesMask; @@ -311,14 +320,22 @@ void TestByteOrder() } void usage(const char* name) { - printf("usage: %s \n", name); + char dataFormats[256] = {0}; + size_t needed = dataParamFormats(dataFormats, 256); + if (needed > 256) { + fprintf(stderr, "warning: data format list truncated, needed %zu bytes", + needed); + } + printf("usage: %s \n", name); printf("OPTIONS:\n"); printf("\t--symlinks, -s : how to handle symlinks\n"); printf("\t in the input directory in command `addall`\n"); printf("\t--special-modes, -m : whether to chmod files in the volume\n"); printf("\t when they are recognized with a name or path\n"); printf("\t where OS setup or iPhone jailbreaking would\n"); - printf("\t require special permissions; specific to `addall`"); + printf("\t require special permissions; specific to `addall`\n"); + printf("\t--data-format, -d `setattr` only: encoding format for xattr value\n"); + printf("\t known formats: %s\n", dataFormats); } IncomingSymlinksPolicy must_parse_symlink_policy(const char* policy, const char* bin_name) { @@ -360,6 +377,7 @@ int main(int argc, char *argv[]) { // Default values, may be overridden by flags IncomingSymlinksPolicy symlink_policy = kIncomingSymlinksTraverse; char assign_special_permissions = TRUE; + DataParamParserPtr data_param_parser = dataParamParserForFormat("literal"); TestByteOrder(); @@ -367,7 +385,8 @@ int main(int argc, char *argv[]) { const struct option longopts[] = { {"symlinks", required_argument, NULL, 's'}, {"special-modes", required_argument, NULL, 'm'}, - {NULL, 0, NULL, 0}, + {"data-format", required_argument, NULL, 'd'}, + {NULL, 0, NULL, 0} }; for( int opt = getopt_long(argc, argv, optstring, longopts, NULL); @@ -381,6 +400,9 @@ int main(int argc, char *argv[]) { case 'm': assign_special_permissions = must_parse_bool(optarg, "special-modes", bin_name); break; + case 'd': + data_param_parser = dataParamParserForFormat(optarg); + break; default: usage(bin_name); exit(2); @@ -432,7 +454,7 @@ int main(int argc, char *argv[]) { } else if (strcmp(argv[2], "getattr") == 0) { cmd_getattr(volume, argc - 2, argv + 2); } else if (strcmp(argv[2], "setattr") == 0) { - cmd_setattr(volume, argc - 2, argv + 2); + cmd_setattr(volume, data_param_parser, argc - 2, argv + 2); } else if (strcmp(argv[2], "debug") == 0) { if (argc > 3 && strcmp(argv[3], "verbose") == 0) { debugBTree(volume->catalogTree, TRUE); diff --git a/third_party/libdmg-hfsplus/src/includes/sizedbuf.h b/third_party/libdmg-hfsplus/src/includes/sizedbuf.h index 2d6a770d150f5..9d5817de2f7aa 100644 --- a/third_party/libdmg-hfsplus/src/includes/sizedbuf.h +++ b/third_party/libdmg-hfsplus/src/includes/sizedbuf.h @@ -2,6 +2,7 @@ #define _LIBDMG_HFSPLUS_SIZEDBUF_H #include +#include // SizedBuf is an arbitrary-length buffer for arbitrary data. SizedBuf // instances can be allocated with AllocBuf, ZAllocBuf, AllocCopyBuf, or @@ -14,7 +15,7 @@ typedef struct { size_t cap; // Arbitrary data storage; allocated length is `cap`. - char data[]; + uint8_t data[]; } SizedBuf; #ifdef __cplusplus @@ -32,13 +33,21 @@ SizedBuf* AllocBufCopy(const SizedBuf* buf); // AllocBufCopyBytes allocates a new SizedBuf with data copied from the provided // byte buffer. -SizedBuf* AllocBufCopyBytes(const char* data, size_t len); +SizedBuf* AllocBufCopyBytes(const uint8_t* data, size_t len); // AllocBufCopyString allocates a new SizedBuf with data copied from the // provided NUL-terminated C string. The `len` of the buffer excludes the // NUL terminator, but the buffer's `cap` includes it. SizedBuf* AllocBufCopyString(const char* str); +// ReallocBuf uses realloc to resize buf such that is capacity is now `new_cap`. +// If `new_cap` is shorter than `buf->len`, `buf->len` is shortened. If `buf` +// is NULL, this calls `AllocBuf` instead. +// +// Do not use `buf` after realloc. The returned pointer may or may not refer +// to the same memory location. +SizedBuf* ReallocBuf(SizedBuf* buf, size_t new_cap); + #ifdef __cplusplus } // extern "C" #endif