#include <zlib.h>
#include "ss_brotli_patch/ss_brotli_patch.h"
+#include "sha1/sha1.h"
void fd_cleanup(int *fd)
{
const char *dest;
const char *archive_file;
const char *patch_orig;
+ const char *dest_sha1;
enum archive_kind kind;
};
const char *dest = NULL;
const char *archive_file = NULL;
const char *patch_orig = NULL;
+ const char *dest_sha1 = NULL;
enum archive_kind kind = -1;
bool help = false;
{"archive-file", required_argument, NULL, 2 },
{"kind", required_argument, NULL, 3 },
{"patch-orig", required_argument, NULL, 4 },
+ {"dest-sha1", required_argument, NULL, 5 },
{"help", no_argument, NULL, 'h'},
{0}
};
patch_orig = optarg;
break;
+ case 5: // dest-sha1
+ if (dest_sha1 != NULL)
+ return (struct parse_result) { .result = PARSE_REPEATED_ARGUMENT };
+ dest_sha1 = optarg;
+ break;
+
case 'h': // help
if (help)
return (struct parse_result) { .result = PARSE_REPEATED_ARGUMENT };
.dest = dest,
.archive_file = archive_file,
.patch_orig = patch_orig,
+ .dest_sha1 = dest_sha1,
.kind = kind,
};
}
void help(char *name)
{
fprintf(stderr,
- "Usage: %s --archive ARCHIVE --archive-file FILE --dest DESTINATION --kind KIND [--patch-orig ORIGIN]\n"
+ "Usage: %s --archive ARCHIVE --archive-file FILE --dest DESTINATION --kind KIND [--patch-orig ORIGIN] [--sha1 SHA1]\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"
" - raw means that the file should be treated as an image to be written,\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",
+ "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",
name);
}
return 0;
}
+int check_sha1(const char *dest, const char *sha1)
+{
+ const size_t SHA1_LEN = 20;
+ const char HEX_DIGITS[] = "0123456789abcdef";
+
+ if (strlen(sha1) != 2 * SHA1_LEN) {
+ fprintf(stderr, "Invalid SHA1 length\n");
+ return -1;
+ }
+
+ __attribute__((cleanup(fd_cleanup))) int dest_fd = open(dest, O_RDONLY);
+ if (dest_fd == -1) {
+ fprintf(stderr, "Couldn't open the target (errno: %m)\n");
+ return -1;
+ }
+
+ SHA1_CTX context;
+ SHA1Init(&context);
+
+ for (;;) {
+ // 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));
+ 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;
+ }
+ }
+
+ SHA1Update(&context, buf, r_read);
+ }
+}
+
int main(int argc, char **argv)
{
struct parse_result parsed = parse_args(argc, argv);
return EXIT_FAILURE;
}
+ if (parsed.dest_sha1 != NULL) {
+ fprintf(stderr, "Write successful. Now checking SHA1\n");
+ if (check_sha1(parsed.dest, parsed.dest_sha1) != 0) {
+ fprintf(stderr, "Couldn't check SHA1; you might need to restore from the backup!\n");
+ return EXIT_FAILURE;
+ }
+ }
+
fprintf(stderr, "Everything written successfully\n");
return EXIT_SUCCESS;
}