| 544 | /* ── Export ───────────────────────────────────────────────────────── */ |
| 545 | |
| 546 | int cbm_artifact_export(const char *db_path, const char *repo_path, const char *project_name, |
| 547 | int quality) { |
| 548 | clear_export_error(); |
| 549 | |
| 550 | if (!db_path || !repo_path || !project_name) { |
| 551 | return artifact_export_fail("validate_args", NULL, "missing_argument", 0); |
| 552 | } |
| 553 | |
| 554 | /* Ensure .codebase-memory/ directory exists */ |
| 555 | char art_dir[CBM_SZ_4K]; |
| 556 | int dir_len = snprintf(art_dir, sizeof(art_dir), "%s/%s", repo_path, CBM_ARTIFACT_DIR); |
| 557 | if (dir_len < 0 || (size_t)dir_len >= sizeof(art_dir)) { |
| 558 | return artifact_export_fail("prepare_artifact_dir", repo_path, "path_too_long", 0); |
| 559 | } |
| 560 | errno = 0; |
| 561 | if (!cbm_mkdir_p(art_dir, ART_DIR_PERMS)) { |
| 562 | return artifact_export_fail("prepare_artifact_dir", art_dir, "mkdir_or_not_directory", |
| 563 | errno); |
| 564 | } |
| 565 | if (!cbm_is_dir(art_dir)) { |
| 566 | return artifact_export_fail("prepare_artifact_dir", art_dir, "not_directory", 0); |
| 567 | } |
| 568 | |
| 569 | size_t db_size = 0; |
| 570 | char *db_data = NULL; |
| 571 | int compression_level = ART_ZSTD_FAST; |
| 572 | |
| 573 | if (quality == CBM_ARTIFACT_BEST) { |
| 574 | compression_level = ART_ZSTD_BEST; |
| 575 | db_data = prepare_snapshot_db(db_path, &db_size, true); |
| 576 | } else { |
| 577 | /* FAST keeps zstd-3 and its indexes, but still snapshots via |
| 578 | * VACUUM INTO: the raw main-file bytes of a live WAL store are a |
| 579 | * torn copy (#895). */ |
| 580 | db_data = prepare_snapshot_db(db_path, &db_size, false); |
| 581 | } |
| 582 | |
| 583 | if (!db_data || db_size == 0) { |
| 584 | free(db_data); |
| 585 | if (cbm_artifact_export_last_error()) { |
| 586 | return CBM_NOT_FOUND; |
| 587 | } |
| 588 | return artifact_export_fail("read_db", db_path, "empty_or_unreadable", errno); |
| 589 | } |
| 590 | |
| 591 | /* Compress with zstd */ |
| 592 | size_t bound = cbm_zstd_compress_bound(db_size); |
| 593 | char *compressed = malloc(bound); |
| 594 | if (!compressed) { |
| 595 | free(db_data); |
| 596 | return artifact_export_fail("compress", NULL, "alloc_compressed_buffer", 0); |
| 597 | } |
| 598 | |
| 599 | int64_t clen = cbm_zstd_compress(db_data, db_size, compressed, bound, compression_level); |
| 600 | free(db_data); |
| 601 | |
| 602 | if (clen <= 0) { |
| 603 | free(compressed); |
no test coverage detected