diff --git a/third_party/libdmg-hfsplus/src/common/parse_data_param.c b/third_party/libdmg-hfsplus/src/common/parse_data_param.c new file mode 100644 index 0000000000000..93facc069ade9 --- /dev/null +++ b/third_party/libdmg-hfsplus/src/common/parse_data_param.c @@ -0,0 +1,66 @@ +#include "parse_data_param.h" + +#include +#include +#include +#include + +#include "common.h" +#include "sizedbuf.h" + +const DataParamParseFormat kParseFormats[] = { + {.name = "literal", .parser = AllocBufCopyString}}; + +DataParamParserPtr dataParamParserForFormat(const char* format_flag) { + size_t format_count = + sizeof(kParseFormats) / sizeof(DataParamParseFormat); + + for (size_t i = 0; i < format_count; ++i) { + if (strcmp(format_flag, kParseFormats[i].name) == 0) { + return kParseFormats[i].parser; + } + } + + fprintf(stderr, "Unrecognized data format: %s\n", format_flag); + exit(2); +} + +size_t dataParamFormats(char* out_buf, size_t out_len) { + size_t format_count = + sizeof(kParseFormats) / sizeof(DataParamParseFormat); + if (!out_buf) { + ASSERT(out_len == 0, "dataParamFormats: NULL out_buf with nonzero out_len"); + } + size_t total_len = 0; + size_t written_len = 0; + char* write_ptr = out_buf; + + // Character output limit, accounting for a null terminator and for counting + // but not actually writing a ", " before the first item. (Pre-refunding + // reduces conditional logic in the loop.) + size_t remain = out_len + 2 - 1; + + for (size_t i = 0; i < format_count; ++i) { + size_t next_len = strlen(kParseFormats[i].name) + 2; + total_len += next_len; + if (remain >= next_len) { + if (write_ptr != out_buf) { + write_ptr = stpcpy(write_ptr, ", "); + } + write_ptr = stpcpy(write_ptr, kParseFormats[i].name); + remain -= next_len; + } + } + + // If we never wrote any output, make out_buf an empty string if possible. + if (write_ptr == out_buf && out_len > 0) { + *out_buf = '\0'; + } + + // Adjust total_len for the null terminator and the extra ", " for the same + // reason as `remain` (but in the opposite direction). The overcount for ", " + // only applies if at least one format existed, but the format list is a + // constant, so it would be very strange for it to be empty. + ASSERT(total_len > 1, "dataParamFormats knew no formats. Seems like a bug."); + return total_len - 2 + 1; +} diff --git a/third_party/libdmg-hfsplus/src/dmg/dmg.c b/third_party/libdmg-hfsplus/src/dmg/dmg.c index 411d8df2734e3..edeae37d65b1c 100644 --- a/third_party/libdmg-hfsplus/src/dmg/dmg.c +++ b/third_party/libdmg-hfsplus/src/dmg/dmg.c @@ -6,6 +6,7 @@ #include "dmg/dmg.h" #include "dmg/filevault.h" +#include "parse_data_param.h" #include "sizedbuf.h" char endianness; @@ -35,12 +36,19 @@ int buildInOut(const char* source, const char* dest, AbstractFile** in, Abstract } void usage(const char *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 [OPTIONS] [extract|build|build2048|res|iso|dmg|attribute] (partition)\n", name); printf("OPTIONS:\n"); printf("\t--key, -k key\n"); printf("\t--compression, -c compressor name (%s)\n", compressionNames()); printf("\t--level, -l compression level\n"); printf("\t--run-sectors, -r run size (in sectors)\n"); + printf("\t--data-format, -d encoding format for attribution or sentinel data (attribute command only)\n"); + printf("\t supported formats: %s\n", dataFormats); exit(2); } @@ -54,16 +62,18 @@ int main(int argc, char* argv[]) { Compressor comp = {.level = COMPRESSION_LEVEL_DEFAULT }; int ret; int runSectors = DEFAULT_SECTORS_AT_A_TIME; + DataParamParserPtr data_param_parser = dataParamParserForFormat("literal"); TestByteOrder(); initDefaultCompressor(&comp); - const char *optstring = "k:c:l:r:"; + const char *optstring = "k:c:l:r:d:"; const struct option longopts[] = { {"key", required_argument, NULL, 'k'}, {"compression", required_argument, NULL, 'c'}, {"level", required_argument, NULL, 'l'}, {"run-sectors", required_argument, NULL, 'r'}, + {"data-format", required_argument, NULL, 'd'}, {NULL, 0, NULL, 0}, }; @@ -89,6 +99,9 @@ int main(int argc, char* argv[]) { return 2; } break; + case 'd': + data_param_parser = dataParamParserForFormat(optarg); + break; default: usage(argv[0]); } @@ -133,8 +146,8 @@ int main(int argc, char* argv[]) { printf("Not enough arguments: attribute "); return 2; } - SizedBuf* anchorBuf = AllocBufCopyString(argv[optind++]); - SizedBuf* dataBuf = AllocBufCopyString(argv[optind++]); + SizedBuf* anchorBuf = data_param_parser(argv[optind++]); + SizedBuf* dataBuf = data_param_parser(argv[optind++]); updateAttributionFromBufs(in, out, anchorBuf, dataBuf); free(dataBuf); free(anchorBuf); diff --git a/third_party/libdmg-hfsplus/src/includes/parse_data_param.h b/third_party/libdmg-hfsplus/src/includes/parse_data_param.h new file mode 100644 index 0000000000000..876d670b8b273 --- /dev/null +++ b/third_party/libdmg-hfsplus/src/includes/parse_data_param.h @@ -0,0 +1,47 @@ +#ifndef _LIBDMG_HFSPLUS_PARSE_DATA_PARAM_H +#define _LIBDMG_HFSPLUS_PARSE_DATA_PARAM_H + +#include "sizedbuf.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// `DataParamParserPtr` points to some function for interpreting a C string +// (typically provided via `argv`) as some byte sequence. Unlike a raw C string, +// the decoded version may contain bytes with value 0. +// +// Ownership of the returned `SizedBuf` is transfered to the caller. +// +// A `DataParamParser` never returns `NULL`. If it receives an unparseable +// encoding or a `NULL` pointer, the program exits with a nonzero return value. +typedef SizedBuf* (*DataParamParserPtr)(const char*); + +// `DataParamParseFormat` is a named algorithm for parsing C strings into byte +// sequences. +typedef struct { + const char* name; + const DataParamParserPtr parser; +} DataParamParseFormat; + +// Scans kLibdmgHfsplusDataParseFormats for the parser matching the name +// provided. If it can't find one, the program exits with return value 2. +DataParamParserPtr dataParamParserForFormat(const char* format_flag); + +// Writes a C string containing a comma-delimited printable list of known data +// format names into the provided buffer of the specified length. If not all +// names fit, it writes as many complete names as it can. If `out_len` is +// nonzero, `out_buf` will be the head of a valid C string when this function +// returns, regardless of whether all formats were named. (If no names at all +// fit, this string will be empty.) +// +// If `out_len` is 0, `*out_buf` is not evaluated. It is valid to provide a +// NULL pointer for `out_buf`, provided that `out_len` is 0, to ask how large +// a buffer would be required to capture all names. +size_t dataParamFormats(char* out_buf, size_t out_len); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _LIBDMG_HFSPLUS_PARSE_DATA_PARAM_H