PARSE_MISSING_ARGUMENT,
PARSE_PATCH_MISTAKE,
PARSE_BAD_KIND,
+ PARSE_BAD_SIZE,
PARSE_NO_PARSE,
} result;
const char *archive_file;
const char *patch_orig;
const char *dest_sha1;
+ size_t dest_size;
enum archive_kind kind;
};
const char *archive_file = NULL;
const char *patch_orig = NULL;
const char *dest_sha1 = NULL;
+ long long int dest_size = 0;
enum archive_kind kind = -1;
bool help = false;
{"kind", required_argument, NULL, 3 },
{"patch-orig", required_argument, NULL, 4 },
{"dest-sha1", required_argument, NULL, 5 },
+ {"dest-size", required_argument, NULL, 6 },
{"help", no_argument, NULL, 'h'},
{0}
};
dest_sha1 = optarg;
break;
+ case 6: // dest-size
+ if (dest_size != 0)
+ return (struct parse_result) { .result = PARSE_REPEATED_ARGUMENT };
+ dest_size = atoi(optarg);
+ if (dest_size <= 0 || dest_size > SIZE_MAX)
+ return (struct parse_result) { .result = PARSE_BAD_SIZE };
+ break;
+
case 'h': // help
if (help)
return (struct parse_result) { .result = PARSE_REPEATED_ARGUMENT };
.archive_file = archive_file,
.patch_orig = patch_orig,
.dest_sha1 = dest_sha1,
+ .dest_size = dest_size,
.kind = kind,
};
}
void help(char *name)
{
fprintf(stderr,
- "Usage: %s --archive ARCHIVE --archive-file FILE --dest DESTINATION --kind KIND [--patch-orig ORIGIN] [--dest-sha1 SHA1]\n"
+ "Usage: %s --archive ARCHIVE --archive-file FILE --dest DESTINATION --kind KIND [--patch-orig ORIGIN] [--dest-sha1 SHA1] [--dest-size SIZE]\n"
"\n"
"Patches a partition using an archive.\n"
"It will look for a file named FILE in an archive called ARCHIVE; the archive should have an extension\n"
" - ss_brotli_patch means that the file should be treated as a delta in the (Tizen-specific) ss_brotli_patch\n"
" format. In this case, ORIGIN is needed and specifies the partition that a delta is based on.\n"
"The results will be written to the DESTINATION partition.\n"
- "If SHA1 is provided (lowercase hexadecimal), it is compared to the one calculated on DESTINATION after writing.\n",
+ "If SHA1 is provided (lowercase hexadecimal), it is compared to the one calculated on DESTINATION after writing.\n"
+ "Additionally, if SIZE is provided, it is used as the destination size; this matters for SHA1 comparision,\n"
+ "and also if ss_brotli_patch is used.\n",
name);
}
return 0;
}
-int check_sha1(const char *dest, const char *sha1)
+int check_sha1(const char *dest, const char *sha1, size_t read_bytes)
{
const size_t SHA1_LEN = 20;
const char HEX_DIGITS[] = "0123456789abcdef";
SHA1_CTX context;
SHA1Init(&context);
- for (;;) {
+ if (read_bytes == 0)
+ read_bytes = SIZE_MAX;
+
+ while (read_bytes > 0) {
// This size doesn't really matter, so let's just pick a value that is big enough
// to not use too many syscalls.
unsigned char buf[1 << 16];
- int r_read = read(dest_fd, buf, sizeof(buf));
+ int r_read = read(dest_fd, buf, read_bytes < sizeof(buf) ? read_bytes : sizeof(buf));
+ read_bytes -= r_read;
switch (r_read) {
case -1:
fprintf(stderr, "Couldn't read from the destination (errno: %m)\n");
return -1;
case 0:
// We read all the data. Let's check the results!
- {
- unsigned char correct_sha1[SHA1_LEN + 1];
- SHA1Final(correct_sha1, &context);
-
- char correct_sha1_hex[2 * SHA1_LEN + 1];
- for (int i = 0; i < SHA1_LEN; ++i) {
- correct_sha1_hex[2 * i] = HEX_DIGITS[correct_sha1[i] / 16];
- correct_sha1_hex[2 * i + 1] = HEX_DIGITS[correct_sha1[i] % 16];
- }
- correct_sha1_hex[sizeof(correct_sha1_hex) - 1] = '\0';
-
- if (strcmp(correct_sha1_hex, sha1) != 0) {
- fprintf(stderr, "Malformed or invalid SHA1: correct one is %s\n", correct_sha1_hex);
- return -1;
- }
-
- return 0;
- }
+ // ... wait, there is no two-layer (switch and while) break in C.
+ // Ugh, we have to goto instead.
+ goto while_done;
}
SHA1Update(&context, buf, r_read);
}
+while_done:;
+
+ unsigned char correct_sha1[SHA1_LEN + 1];
+ SHA1Final(correct_sha1, &context);
+
+ char correct_sha1_hex[2 * SHA1_LEN + 1];
+ for (int i = 0; i < SHA1_LEN; ++i) {
+ correct_sha1_hex[2 * i] = HEX_DIGITS[correct_sha1[i] / 16];
+ correct_sha1_hex[2 * i + 1] = HEX_DIGITS[correct_sha1[i] % 16];
+ }
+ correct_sha1_hex[sizeof(correct_sha1_hex) - 1] = '\0';
+
+ if (strcmp(correct_sha1_hex, sha1) != 0) {
+ fprintf(stderr, "Malformed or invalid SHA1: correct one is %s\n", correct_sha1_hex);
+ return -1;
+ }
+
+ return 0;
}
int main(int argc, char **argv)
fprintf(stderr, "Invalid `kind` parameter (possible: `raw`, `ss_brotli_patch`)\n");
help(argv[0]);
return EXIT_FAILURE;
+ case PARSE_BAD_SIZE:
+ fprintf(stderr, "Invalid `dest-size` parameter (it should be a positive number that fits in size_t)\n");
+ help(argv[0]);
+ return EXIT_FAILURE;
case PARSE_NO_PARSE:
fprintf(stderr, "Invalid parameters passed\n");
help(argv[0]);
break;
case KIND_BROTLI_PATCH:
- r = apply_patch_brotli(parsed.patch_orig, parsed.dest, tar);
+ r = apply_patch_brotli(parsed.patch_orig, parsed.dest, tar, parsed.dest_size);
break;
}
if (parsed.dest_sha1 != NULL) {
fprintf(stderr, "Write successful. Now checking SHA1\n");
- if (check_sha1(parsed.dest, parsed.dest_sha1) != 0) {
+ if (check_sha1(parsed.dest, parsed.dest_sha1, parsed.dest_size) != 0) {
fprintf(stderr, "Couldn't check SHA1; you might need to restore from the backup!\n");
return EXIT_FAILURE;
}
return PF_OK;
}
-static int open_files(struct bs_data *data, const char *source_file, const char *dest_file, TAR *patch_tar)
+static int open_files(struct bs_data *data, const char *source_file, const char *dest_file, TAR *patch_tar, size_t dest_size)
{
assert(data);
assert(source_file);
data->src_len = get_file_len(data->src_fd);
data->patch_len = th_get_size(data->patch_tar);
- data->dest_len = get_file_len(data->dest_fd);
+ data->dest_len = dest_size == 0 ? get_file_len(data->dest_fd) : dest_size;
data->src_ptr = mmap(NULL, data->src_len, PROT_READ, MAP_PRIVATE, data->src_fd, 0);
if (data->src_ptr == MAP_FAILED) {
return PF_OK;
}
-int apply_patch_brotli(const char *source_file, const char *dest_file, TAR *patch_tar)
+int apply_patch_brotli(const char *source_file, const char *dest_file, TAR *patch_tar, size_t dest_size)
{
assert(source_file);
assert(dest_file);
init_data(&data);
- if ((result = open_files(&data, source_file, dest_file, patch_tar)) != PF_OK)
+ if ((result = open_files(&data, source_file, dest_file, patch_tar, dest_size)) != PF_OK)
goto exit;
if ((result = decompress_bytes(&data, 0)) != PF_OK)