const char *patch_orig;
const char *dest_sha1;
size_t dest_size;
+ bool write_all;
enum archive_kind kind;
};
long long int dest_size = 0;
enum archive_kind kind = -1;
bool help = false;
+ bool write_all = true;
for (;;) {
const struct option long_options[] = {
{"patch-orig", required_argument, NULL, 4 },
{"dest-sha1", required_argument, NULL, 5 },
{"dest-size", required_argument, NULL, 6 },
+ {"no-write-all", no_argument, NULL, 'n'},
{"help", no_argument, NULL, 'h'},
{0}
};
- int option = getopt_long(argc, argv, "h", long_options, NULL);
+ int option = getopt_long(argc, argv, "hn", long_options, NULL);
if (option < 0)
break;
return (struct parse_result) { .result = PARSE_BAD_SIZE };
break;
+ case 'n': // no-write-all
+ if (!write_all)
+ return (struct parse_result) { .result = PARSE_REPEATED_ARGUMENT };
+ write_all = false;
+ break;
+
case 'h': // help
if (help)
return (struct parse_result) { .result = PARSE_REPEATED_ARGUMENT };
.dest_sha1 = dest_sha1,
.dest_size = dest_size,
.kind = kind,
+ .write_all = write_all,
};
}
void help(char *name)
{
fprintf(stderr,
- "Usage: %s --archive ARCHIVE --archive-file FILE --dest DESTINATION --kind KIND [--patch-orig ORIGIN] [--dest-sha1 SHA1] [--dest-size SIZE]\n"
+ "Usage: %s --archive ARCHIVE --archive-file FILE --dest DESTINATION --kind KIND [--patch-orig ORIGIN] [--dest-sha1 SHA1] [--dest-size SIZE] [--no-write-all]\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"
"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"
"Additionally, if SIZE is provided, it is used as the destination size; this matters for SHA1 comparision,\n"
- "and also if ss_brotli_patch.\n",
+ "and also if ss_brotli_patch.\n"
+ "In case the KIND is ss_brotli_path and the DESTINATION is the clone of the ORIGIN you can specify\n"
+ "the --no-write-all flag to not copy the data from the source, because it is already there.\n",
name);
}
case KIND_BROTLI_PATCH:
{
struct dec_funcs funcs = { init_brotli, decompress_brotli, free_brotli };
- r = apply_patch(parsed.patch_orig, parsed.dest, tar, parsed.dest_size, NULL, &funcs);
+ r = apply_patch(parsed.patch_orig, parsed.dest, tar, parsed.dest_size, NULL, parsed.write_all, &funcs);
break;
}
}
return PF_OK;
}
-int apply_patch(const char *source_file, const char *dest_file, TAR *patch_tar, size_t dest_size, size_t *free_space, struct dec_funcs *funcs)
+int apply_patch(const char *source_file, const char *dest_file, TAR *patch_tar, size_t dest_size, size_t *free_space, bool write_all, struct dec_funcs *funcs)
{
assert(source_file);
assert(dest_file);
goto exit;
if ((result = remmap_if_needed(&data.dest)) != PF_OK)
goto exit;
- *data.dest.pos = *(uint8_t*)data.src.pos + *(uint8_t*)buff_out_pos;
+ /*
+ * If *buff_out_pos == 0 and old_pos == new_pos then it is basically a data copy.
+ * In case the destination partition is the clone of the source partition,
+ * then there is no need to copy data again, because it is already in place.
+ */
+ if (write_all || old_pos != new_pos || *(uint8_t*)buff_out_pos != 0) {
+ *data.dest.pos = *(uint8_t*)data.src.pos + *(uint8_t*)buff_out_pos;
+ }
data.dest.pos++;
data.src.pos++;
buff_out_pos++;
#include "brotli.h"
-int apply_patch_brotli(const char *source_file, const char *dest_file, TAR *patch_tar, size_t dest_size, size_t *free_space)
+int apply_patch_brotli(const char *source_file, const char *dest_file, TAR *patch_tar, size_t dest_size, size_t *free_space, bool write_all)
{
struct dec_funcs funcs = { init_brotli, decompress_brotli, free_brotli };
- return apply_patch(source_file, dest_file, patch_tar, dest_size, free_space, &funcs);
+ return apply_patch(source_file, dest_file, patch_tar, dest_size, free_space, write_all, &funcs);
}