Felipe97/llama-cpp-compiled
01.2k
1#include "ggml.h"2#include "gguf.h"3 4#include <algorithm>5#include <clocale>6#include <cstddef>7#include <cstdio>8#include <cstdlib> /* abort() */9#include <cstring>10#include <stdexcept>11#include <string>12 13#include <sstream>14#include <fstream>15 16#ifdef __cplusplus17extern "C" {18#endif19 20#include "hash/xxhash/xxhash.h"21#include "hash/sha256/sha256.h"22 23#ifdef __cplusplus24}25#endif26 27// sha1 is compiled as C++ and lives in a namespace, see scripts/sync_vendor.py28#include "hash/sha1/sha1.h"29using namespace vendor_hash;30 31 32// uuid.uuid5(uuid.NAMESPACE_URL, 'en.wikipedia.org/wiki/Llama.cpp')33#define UUID_NAMESPACE_LLAMA_CPP "ef001206-dadc-5f6d-a15f-3359e577d4e5"34#define UUID_NAMESPACE_LLAMA_CPP_HEX 0xef, 0x00, 0x12, 0x06, 0xda, 0xdc, 0x5f, 0x6d, 0xa1, 0x5f, 0x33, 0x59, 0xe5, 0x77, 0xd4, 0xe535 36 37#define HASH_TYPE_SHA256_STR "sha256"38#define HASH_TYPE_SHA1_STR "sha1"39#define HASH_TYPE_XXH64_STR "xxh64"40#define HASH_TYPE_UUID_STR "uuid"41 42 43typedef enum {44 HASH_EXIT_SUCCESS = 0, // All hash has been generated or validated45 HASH_EXIT_FAILURE = 1, // Generic Failure46 HASH_EXIT_MISMATCH = 2, // Hash mismatched during validation47 HASH_EXIT_MANIFEST_MISSING_ENTRY = 3, // Hash attempted validation but missing entry in manifest48 HASH_EXIT_MANIFEST_UNKNOWN_HASH = 4, // Manifest is present, but we do not know any hash format within it49 HASH_EXIT_MANIFEST_FILE_ERROR = 5 // Manifest is either missing or not a known format50} hash_exit_code_t;51 52 53typedef enum {54 HASH_MANIFEST_NOT_FOUND,55 HASH_MANIFEST_MISMATCH,56 HASH_MANIFEST_OK,57} hash_manifest_result_t;58 59 60struct hash_params {61 std::string input;62 bool xxh64 = false;63 bool sha1 = false;64 bool sha256 = false;65 bool uuid = false;66 67 bool no_layer = false;68 69 bool manifest_is_usable = false;70 std::string manifest_file;71};72 73struct manifest_check_params {74 bool xxh64 = false;75 bool sha1 = false;76 bool sha256 = false;77 bool uuid = false;78};79 80static char const * hash_manifest_result_to_str(hash_manifest_result_t value) {81 switch (value) {82 case HASH_MANIFEST_NOT_FOUND: return "Not Found";83 case HASH_MANIFEST_MISMATCH: return "Mismatch";84 case HASH_MANIFEST_OK: return "Ok";85 }86 return "?";87}88 89static char const * hash_exit_code_to_str(hash_exit_code_t value) {90 switch (value) {91 case HASH_EXIT_SUCCESS: return "Success";92 case HASH_EXIT_FAILURE: return "Failure";93 case HASH_EXIT_MISMATCH: return "Mismatch";94 case HASH_EXIT_MANIFEST_MISSING_ENTRY: return "Manifest Missing Entry";95 case HASH_EXIT_MANIFEST_UNKNOWN_HASH: return "Manifest Unknown Hash";96 case HASH_EXIT_MANIFEST_FILE_ERROR: return "Manifest File Error";97 }98 return "?";99}100 101static void hash_print_usage(const char * executable) {102 const hash_params default_params;103 printf("\n");104 printf("usage: %s [options] GGUF_IN\n", executable);105 printf("\n");106 printf("Hash a GGUF file");107 printf("\n");108 printf("options:\n");109 printf(" -h, --help show this help message and exit\n");110 printf(" --xxh64 use xxh64 hash\n");111 printf(" --sha1 use sha1 hash\n");112 printf(" --sha256 use sha256 hash\n");113 printf(" --all use all hash\n");114 printf(" --no-layer exclude per layer hash\n");115 printf(" --uuid generate UUIDv5 ID\n");116 printf(" -c, --check <manifest> verify against a manifest\n");117 printf("\n");118}119 120static void hash_params_parse_ex(int argc, const char ** argv, hash_params & params) {121 std::string arg;122 bool invalid_param = false;123 const std::string arg_prefix = "--";124 125 int arg_idx = 1;126 for (; arg_idx < argc && strncmp(argv[arg_idx], "--", 2) == 0; arg_idx++) {127 arg = argv[arg_idx];128 if (arg.compare(0, arg_prefix.size(), arg_prefix) == 0) {129 std::replace(arg.begin(), arg.end(), '_', '-');130 }131 132 bool arg_found = false;133 if (arg == "-h" || arg == "--help") {134 hash_print_usage(argv[0]);135 exit(0);136 }137 138 if (arg == "--xxh64") {139 arg_found = true;140 params.xxh64 = true;141 }142 143 if (arg == "--sha1") {144 arg_found = true;145 params.sha1 = true;146 }147 148 if (arg == "--uuid") {149 arg_found = true;150 params.uuid = true;151 }152 153 if (arg == "--sha256") {154 arg_found = true;155 params.sha256 = true;156 }157 158 if (arg == "--all") {159 arg_found = true;160 params.sha256 = true;161 params.sha1 = true;162 params.xxh64 = true;163 }164 165 if (arg == "--no-layer") {166 arg_found = true;167 params.no_layer = true;168 }169 170 if (arg == "-c" || arg == "--check") {171 if (++arg_idx >= argc) {172 invalid_param = true;173 break;174 }175 arg_found = true;176 params.manifest_file = argv[arg_idx];177 }178 179 if (!arg_found) {180 throw std::invalid_argument("error: unknown argument: " + arg);181 }182 }183 184 if (invalid_param) {185 throw std::invalid_argument("error: invalid parameter for argument:" + arg);186 }187 188 if (argc - arg_idx < 1) {189 throw std::invalid_argument("error: bad arguments");190 }191 192 params.input = argv[arg_idx++];193}194 195static bool hash_params_parse(int argc, const char ** argv, hash_params & params) {196 bool result = true;197 try {198 hash_params_parse_ex(argc, argv, params);199 }200 catch (const std::invalid_argument & ex) {201 fprintf(stderr, "%s\n", ex.what());202 hash_print_usage(argv[0]);203 exit(EXIT_FAILURE);204 }205 return result;206}207 208static bool manifest_type(const std::string & manifest_file, manifest_check_params & manifest_check) {209 if (manifest_file.empty()) {210 return false;211 }212 213 std::ifstream file(manifest_file);214 if (!file.is_open()) {215 return false;216 }217 218 std::string manifest_entry_line;219 while (getline(file, manifest_entry_line)) {220 // hash_type_str hash_str tensor_name221 // e.g. 'xxh64 f66e9cd66a4396a0 test.gguf:tensor_0'222 std::istringstream line_stream(manifest_entry_line);223 std::string file_hash_type;224 if (line_stream >> file_hash_type) {225 if (file_hash_type == HASH_TYPE_SHA256_STR) {226 manifest_check.sha256 = true;227 } else if (file_hash_type == HASH_TYPE_SHA1_STR) {228 manifest_check.sha1 = true;229 } else if (file_hash_type == HASH_TYPE_XXH64_STR) {230 manifest_check.xxh64 = true;231 } else if (file_hash_type == HASH_TYPE_UUID_STR) {232 manifest_check.uuid = true;233 }234 }235 }236 237 return true;238}239 240static hash_manifest_result_t manifest_verify(const std::string& manifest_file, const std::string& hash_type_str, const std::string& hash_str, const std::string& tensor_name) {241 if (manifest_file.empty()) {242 return HASH_MANIFEST_NOT_FOUND;243 }244 245 std::ifstream file(manifest_file);246 if (!file.is_open()) {247 return HASH_MANIFEST_NOT_FOUND;248 }249 250 std::string manifest_entry_line;251 while (getline(file, manifest_entry_line)) {252 std::istringstream line_stream(manifest_entry_line);253 std::string file_hash_type;254 std::string file_hash;255 std::string file_tensor_name;256 if (line_stream >> file_hash_type >> file_hash >> file_tensor_name) {257 // Line parsed. Check hash validity258 259 if (file_hash_type != hash_type_str) {260 continue;261 }262 263 if (file_tensor_name != tensor_name) {264 continue;265 }266 267 return (file_hash == hash_str) ? HASH_MANIFEST_OK : HASH_MANIFEST_MISMATCH;268 }269 }270 271 return HASH_MANIFEST_NOT_FOUND;272}273 274static void generate_uuidv5(const unsigned char sha1_digest[20], unsigned char uuid[16]) {275 // Ref: https://www.rfc-editor.org/rfc/rfc9562.html#section-5.5276 // Assumes that digest was processed correctly with the expected namespace277 for (int i = 0; i < 16; i++) {278 uuid[i] = sha1_digest[i];279 }280 281 // Set bits corresponding to UUID ver 5282 uuid[ 6] &= ~(0xF << 4);283 uuid[ 6] |= (5 << 4);284 285 // Set bits corresponding to UUID variant 0b10XX286 uuid[ 8] &= ~(0xc << 4);287 uuid[ 8] |= (0x8 << 4);288}289 290static hash_exit_code_t gguf_hash(const hash_params & hash_params) {291 const std::string & fname = hash_params.input;292 struct ggml_context * ctx_data = NULL;293 294 struct gguf_init_params params = {295 /*.no_alloc = */ false,296 /*.ctx = */ &ctx_data,297 };298 299 // xxh64 init300 XXH64_state_t* xxh64_model_hash_state = NULL;301 if (hash_params.xxh64) {302 xxh64_model_hash_state = XXH64_createState();303 if (xxh64_model_hash_state==NULL) {304 abort();305 }306 307 XXH64_hash_t const seed = 0;308 if (XXH64_reset(xxh64_model_hash_state, seed) == XXH_ERROR) {309 abort();310 }311 }312 313 // sha1 init314 SHA1_CTX sha1_model_hash_ctx;315 if (hash_params.sha1) {316 SHA1Init(&sha1_model_hash_ctx);317 }318 319 // sha256 init320 sha256_t sha256_model_hash_ctx;321 if (hash_params.sha256) {322 sha256_init(&sha256_model_hash_ctx);323 }324 325 // sha1 for uuid init326 SHA1_CTX sha1_for_uuid_ctx;327 if (hash_params.uuid) {328 unsigned char const uuidv5_namespace[] = {UUID_NAMESPACE_LLAMA_CPP_HEX};329 SHA1Init(&sha1_for_uuid_ctx);330 SHA1Update( &sha1_for_uuid_ctx, (unsigned char const *)uuidv5_namespace, sizeof(uuidv5_namespace));331 }332 333 struct gguf_context * ctx = gguf_init_from_file(fname.c_str(), params);334 const int n_tensors = gguf_get_n_tensors(ctx);335 bool tensor_layer_in_manifest = false;336 bool model_in_manifest = false;337 bool tensor_layer_has_mismatch = false;338 bool model_has_mismatch = false;339 for (int i = 0; i < n_tensors; ++i) {340 const char * name = gguf_get_tensor_name(ctx, i);341 struct ggml_tensor * cur = ggml_get_tensor(ctx_data, name);342 auto n_bytes = ggml_nbytes(cur);343 auto *raw_data = cur->data;344 const std::string tensor_layer_name = fname + ":" + name;345 346 if (hash_params.xxh64) {347 348 if (!hash_params.no_layer) {349 // Per Layer Hash350 XXH64_hash_t hash = XXH64(raw_data, n_bytes, 0);351 352 char hex_result[17];353 for (int offset = 0; offset < 8; offset++) {354 unsigned int shift_bits_by = (8 * (8 - offset - 1));355 snprintf( ( hex_result + (2*offset)), sizeof(hex_result) - (2*offset), "%02x", (unsigned char) (hash >> shift_bits_by)&0xff);356 }357 358 if (hash_params.manifest_is_usable) {359 hash_manifest_result_t verify_result = manifest_verify(hash_params.manifest_file, HASH_TYPE_XXH64_STR, hex_result, tensor_layer_name);360 361 switch (verify_result) {362 case HASH_MANIFEST_NOT_FOUND:363 break;364 case HASH_MANIFEST_MISMATCH:365 tensor_layer_in_manifest = true;366 tensor_layer_has_mismatch = true;367 break;368 case HASH_MANIFEST_OK:369 tensor_layer_in_manifest = true;370 break;371 }372 373 printf("%-8s %-s %s - %s\n", HASH_TYPE_XXH64_STR, hex_result, tensor_layer_name.c_str(), hash_manifest_result_to_str(verify_result));374 } else {375 printf("%-8s %-s %s\n", HASH_TYPE_XXH64_STR, hex_result, tensor_layer_name.c_str());376 }377 }378 379 // Overall Model Hash380 if (XXH64_update(xxh64_model_hash_state, raw_data, n_bytes) == XXH_ERROR) abort();381 }382 383 if (hash_params.sha1) {384 385 if (!hash_params.no_layer) {386 // Per Layer Hash387 char result[21]; // sha1 outputs 20 bytes388 SHA1( result, (const char *)raw_data, n_bytes);389 390 char hex_result[41] = {0};391 for (int offset = 0; offset < 20; offset++) {392 snprintf( ( hex_result + (2*offset)), sizeof(hex_result) - (2*offset), "%02x", result[offset]&0xff);393 }394 395 if (hash_params.manifest_is_usable) {396 hash_manifest_result_t verify_result = manifest_verify(hash_params.manifest_file, HASH_TYPE_SHA1_STR, hex_result, tensor_layer_name);397 398 switch (verify_result) {399 case HASH_MANIFEST_NOT_FOUND:400 break;401 case HASH_MANIFEST_MISMATCH:402 tensor_layer_in_manifest = true;403 tensor_layer_has_mismatch = true;404 break;405 case HASH_MANIFEST_OK:406 tensor_layer_in_manifest = true;407 break;408 }409 410 printf("%-8s %-s %s - %s\n", HASH_TYPE_SHA1_STR, hex_result, tensor_layer_name.c_str(), hash_manifest_result_to_str(verify_result));411 } else {412 printf("%-8s %-s %s\n", HASH_TYPE_SHA1_STR, hex_result, tensor_layer_name.c_str());413 }414 }415 416 // Overall Model Hash417 SHA1Update( &sha1_model_hash_ctx, (unsigned char const *)raw_data, n_bytes);418 }419 420 if (hash_params.sha256) {421 422 if (!hash_params.no_layer) {423 // Per Layer Hash424 unsigned char result[SHA256_DIGEST_SIZE]; // sha256 outputs 32 bytes425 sha256_hash((unsigned char*) result, (const unsigned char *)raw_data, n_bytes);426 427 char hex_result[SHA256_DIGEST_SIZE * 2 + 1] = {0};428 for (int offset = 0; offset < SHA256_DIGEST_SIZE; offset++) {429 snprintf( ( hex_result + (2*offset)), sizeof(hex_result) - (2*offset), "%02x", result[offset]&0xff);430 }431 432 if (hash_params.manifest_is_usable) {433 hash_manifest_result_t verify_result = manifest_verify(hash_params.manifest_file, HASH_TYPE_SHA256_STR, hex_result, tensor_layer_name);434 435 switch (verify_result) {436 case HASH_MANIFEST_NOT_FOUND:437 break;438 case HASH_MANIFEST_MISMATCH:439 tensor_layer_in_manifest = true;440 tensor_layer_has_mismatch = true;441 break;442 case HASH_MANIFEST_OK:443 tensor_layer_in_manifest = true;444 break;445 }446 447 printf("%-8s %-s %s - %s\n", HASH_TYPE_SHA256_STR, hex_result, tensor_layer_name.c_str(), hash_manifest_result_to_str(verify_result));448 } else {449 printf("%-8s %-s %s\n", HASH_TYPE_SHA256_STR, hex_result, tensor_layer_name.c_str());450 }451 }452 453 // Overall Model Hash454 sha256_update( &sha256_model_hash_ctx, (unsigned char const *)raw_data, n_bytes);455 }456 457 if (hash_params.uuid) {458 SHA1Update( &sha1_for_uuid_ctx, (unsigned char const *)raw_data, n_bytes);459 }460 }461 462 if (hash_params.xxh64) {463 XXH64_hash_t const hash = XXH64_digest(xxh64_model_hash_state);464 465 char hex_result[17];466 for (int offset = 0; offset < 8; offset++) {467 unsigned int shift_bits_by = (8 * (8 - offset - 1));468 snprintf( ( hex_result + (2*offset)), sizeof(hex_result) - (2*offset), "%02x", (unsigned char) (hash >> shift_bits_by)&0xff);469 }470 471 if (hash_params.manifest_is_usable) {472 hash_manifest_result_t verify_result = manifest_verify(hash_params.manifest_file, HASH_TYPE_XXH64_STR, hex_result, fname);473 474 switch (verify_result) {475 case HASH_MANIFEST_NOT_FOUND:476 break;477 case HASH_MANIFEST_MISMATCH:478 model_in_manifest = true;479 model_has_mismatch = true;480 break;481 case HASH_MANIFEST_OK:482 model_in_manifest = true;483 break;484 }485 486 printf("%-8s %-s %s - %s\n", HASH_TYPE_XXH64_STR, hex_result, fname.c_str(), hash_manifest_result_to_str(verify_result));487 } else {488 printf("%-8s %-s %s\n", HASH_TYPE_XXH64_STR, hex_result, fname.c_str());489 }490 }491 492 if (hash_params.sha1) {493 unsigned char result[21];494 SHA1Final(result, &sha1_model_hash_ctx);495 496 char hex_result[41];497 for (int offset = 0; offset < 20; offset++) {498 snprintf( ( hex_result + (2*offset)), sizeof(hex_result) - (2*offset), "%02x", result[offset]&0xff);499 }500 501 if (hash_params.manifest_is_usable) {502 hash_manifest_result_t verify_result = manifest_verify(hash_params.manifest_file, HASH_TYPE_SHA1_STR, hex_result, fname);503 504 switch (verify_result) {505 case HASH_MANIFEST_NOT_FOUND:506 break;507 case HASH_MANIFEST_MISMATCH:508 model_in_manifest = true;509 model_has_mismatch = true;510 break;511 case HASH_MANIFEST_OK:512 model_in_manifest = true;513 break;514 }515 516 printf("%-8s %-s %s - %s\n", HASH_TYPE_SHA1_STR, hex_result, fname.c_str(), hash_manifest_result_to_str(verify_result));517 } else {518 printf("%-8s %-s %s\n", HASH_TYPE_SHA1_STR, hex_result, fname.c_str());519 }520 }521 522 if (hash_params.sha256) {523 unsigned char result[SHA256_DIGEST_SIZE]; // sha256 outputs 32 bytes524 sha256_final( &sha256_model_hash_ctx, result);525 526 char hex_result[SHA256_DIGEST_SIZE * 2 + 1] = {0};527 for (int offset = 0; offset < SHA256_DIGEST_SIZE; offset++) {528 snprintf( ( hex_result + (2*offset)), sizeof(hex_result) - (2*offset), "%02x", result[offset]&0xff);529 }530 531 if (hash_params.manifest_is_usable) {532 hash_manifest_result_t verify_result = manifest_verify(hash_params.manifest_file, HASH_TYPE_SHA256_STR, hex_result, fname);533 534 switch (verify_result) {535 case HASH_MANIFEST_NOT_FOUND:536 break;537 case HASH_MANIFEST_MISMATCH:538 model_in_manifest = true;539 model_has_mismatch = true;540 break;541 case HASH_MANIFEST_OK:542 model_in_manifest = true;543 break;544 }545 546 printf("%-8s %-s %s - %s\n", HASH_TYPE_SHA256_STR, hex_result, fname.c_str(), hash_manifest_result_to_str(verify_result));547 } else {548 printf("%-8s %-s %s\n", HASH_TYPE_SHA256_STR, hex_result, fname.c_str());549 }550 }551 552 if (hash_params.uuid) {553 unsigned char result[21];554 SHA1Final(result, &sha1_for_uuid_ctx);555 556 unsigned char uuid[16];557 generate_uuidv5(result, uuid);558 559 char string_buffer[37] = {0};560 snprintf(string_buffer, sizeof(string_buffer), "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",561 uuid[0], uuid[1], uuid[2], uuid[3],562 uuid[4], uuid[5], uuid[6], uuid[7],563 uuid[8], uuid[9], uuid[10], uuid[11],564 uuid[12], uuid[13], uuid[14], uuid[15]);565 566 if (hash_params.manifest_is_usable) {567 hash_manifest_result_t verify_result = manifest_verify(hash_params.manifest_file, HASH_TYPE_SHA256_STR, string_buffer, fname);568 569 switch (verify_result) {570 case HASH_MANIFEST_NOT_FOUND:571 break;572 case HASH_MANIFEST_MISMATCH:573 model_in_manifest = true;574 model_has_mismatch = true;575 break;576 case HASH_MANIFEST_OK:577 model_in_manifest = true;578 break;579 }580 581 printf("%-8s %-s %s - %s\n", HASH_TYPE_UUID_STR, string_buffer, fname.c_str(), hash_manifest_result_to_str(verify_result));582 } else {583 printf("%-8s %-s %s\n", HASH_TYPE_UUID_STR, string_buffer, fname.c_str());584 }585 }586 587 588 ggml_free(ctx_data);589 gguf_free(ctx);590 591 592 if (hash_params.manifest_is_usable) {593 // In hash verification mode594 595 if (!model_in_manifest) {596 // model missing in manifest?597 598 // Check tensor layer...599 if (!tensor_layer_in_manifest) {600 // Still missing? Maybe we are reading the wrong manifest.601 return HASH_EXIT_MANIFEST_MISSING_ENTRY;602 }603 604 if (tensor_layer_has_mismatch) {605 // Per tensor check found error606 return HASH_EXIT_FAILURE;607 }608 609 // All per tensor layer checks passed? Sounds good enough.610 return HASH_EXIT_SUCCESS;611 }612 613 // Overall model check passed, but let's check per layer just in case614 // If missing, we don't care too much as the overall model checked615 if (tensor_layer_in_manifest && tensor_layer_has_mismatch) {616 return HASH_EXIT_FAILURE;617 }618 619 if (model_has_mismatch) {620 // model has failed hash somewhere in the model621 return HASH_EXIT_FAILURE;622 }623 624 // All checks appears to be fine625 return HASH_EXIT_SUCCESS;626 }627 628 // In hash generation mode629 return HASH_EXIT_SUCCESS;630}631 632int main(int argc, const char ** argv) {633 std::setlocale(LC_NUMERIC, "C");634 635 hash_params params;636 manifest_check_params manifest_check;637 hash_params_parse(argc, argv, params);638 639 if (!params.manifest_file.empty()) {640 if (!manifest_type(params.manifest_file, manifest_check)) {641 printf("ERROR cannot open manifest %s", params.manifest_file.c_str());642 return HASH_EXIT_MANIFEST_FILE_ERROR;643 }644 645 if (!manifest_check.sha256 && !manifest_check.sha1 && !manifest_check.xxh64 && !manifest_check.uuid) {646 printf("ERROR manifest does not have any known hash format in %s", params.manifest_file.c_str());647 return HASH_EXIT_MANIFEST_UNKNOWN_HASH;648 }649 650 printf("manifest %s", params.manifest_file.c_str());651 652 if (manifest_check.sha256) {653 printf(" sha256");654 }655 656 if (manifest_check.sha1) {657 printf(" sha1");658 }659 660 if (manifest_check.xxh64) {661 printf(" xxh64");662 }663 664 if (manifest_check.uuid) {665 printf(" uuid");666 }667 668 printf("\n");669 670 // Autoselect the highest security hash if manifest is provided but671 // the user has not specifically defined the hash they care about672 if (!params.xxh64 && !params.sha1 && !params.uuid && !params.sha256) {673 // User has not selected a specific value, pick most secure hash674 if (manifest_check.sha256) {675 params.sha256 = true;676 } else if (manifest_check.sha1) {677 params.sha1 = true;678 } else if (manifest_check.xxh64) {679 params.xxh64 = true;680 } else if (manifest_check.uuid) {681 params.uuid = true;682 }683 }684 685 params.manifest_is_usable = true;686 }687 688 // By default if no swich argument provided, assume xxh64689 if (!params.xxh64 && !params.sha1 && !params.uuid && !params.sha256) {690 params.xxh64 = true;691 }692 693 hash_exit_code_t exit_code = gguf_hash(params);694 695 if (params.manifest_is_usable) {696 printf("\nVerification results for %s - %s\n", params.manifest_file.c_str(), hash_exit_code_to_str(exit_code));697 }698 699 return exit_code;700}701 