diff --git a/tests/salvage/ncp_delete_smoke.c b/tests/salvage/ncp_delete_smoke.c index c0a488d..a55473b 100644 --- a/tests/salvage/ncp_delete_smoke.c +++ b/tests/salvage/ncp_delete_smoke.c @@ -21,7 +21,7 @@ static void usage(const char *prog) { fprintf(stderr, "Usage: %s [--expect-delete CODE] [--create-only] [--delete-only] " - "[ncpfs options] PATH\n" + "[--payload TEXT] [ncpfs options] PATH\n" "\n" "ncpfs options are parsed by ncp_initialize(), for example:\n" " -S SERVER -U USER -P PASSWORD -n\n" @@ -54,6 +54,7 @@ int main(int argc, char **argv) struct ncp_file_info file_info; int create = 1; int delete = 1; + const char *payload = NULL; int i; long err; @@ -83,6 +84,13 @@ int main(int argc, char **argv) } else if (!strcmp(argv[i], "--delete-only")) { create = 0; delete = 1; + } else if (!strcmp(argv[i], "--payload")) { + if (++i >= argc) { + fprintf(stderr, "missing --payload value\n"); + ncp_close(conn); + return 2; + } + payload = argv[i]; } else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) { usage(argv[0]); ncp_close(conn); @@ -112,6 +120,23 @@ int main(int argc, char **argv) ncp_close(conn); return 1; } + if (payload) { + size_t payload_len = strlen(payload); + err = ncp_write(conn, file_info.file_id, 0, payload_len, payload); + if (!err) + err = ncp_write(conn, file_info.file_id, payload_len, 1, "\n"); + if (err) { + fprintf(stderr, + "NCP write failed: path=%s error=0x%04x\n", + path, (unsigned int)err); + ncp_close_file(conn, file_info.file_id); + ncp_close(conn); + return 1; + } + printf("NCP write path=%s bytes=%lu verified\n", + path, (unsigned long)(payload_len + 1)); + } + err = ncp_close_file(conn, file_info.file_id); if (err) { fprintf(stderr, diff --git a/tests/salvage/salvage_smoke_suite.sh b/tests/salvage/salvage_smoke_suite.sh index 330b1c0..8995316 100755 --- a/tests/salvage/salvage_smoke_suite.sh +++ b/tests/salvage/salvage_smoke_suite.sh @@ -1,10 +1,10 @@ #!/usr/bin/env bash # End-to-end mars_nwe salvage smoke suite. # -# The suite purges stale salvage entries through NCP first, then runs two -# create/write/delete/recover cycles. Each cycle cats the .recycle payload -# after delete and the restored live file after recover so content changes are -# visible in the report. +# The suite purges stale salvage entries through NCP first, then creates up to +# three deleted versions through NCP writes/deletes. It cats the live payload, +# the .recycle payload for each version, and the restored live file after +# recover so content changes are visible in the report. set -u @@ -23,6 +23,7 @@ DELETED_BY="" OUT_FILE="" KEEP_GOING=1 CLEAN_ARTIFACTS=1 +CYCLE_COUNT=3 usage() { cat <&2 exit 2 fi +case "$CYCLE_COUNT" in + 1|2|3) ;; + *) echo "--cycles must be 1, 2 or 3" >&2; exit 2 ;; +esac REPORT_TMP=$(mktemp "${TMPDIR:-/tmp}/mars-salvage-suite.XXXXXX") FAILURES=0 @@ -188,43 +194,31 @@ prepare_paths() { DIRNAME=$(dirname -- "$RELATIVE_PATH") [ "$DIRNAME" = "." ] && DIRNAME="" COPY_NAME="Copy #1 of $BASENAME" + COPY2_NAME="Copy #2 of $BASENAME" SCAN_PATH=$(netware_directory_path "$NETWARE_PATH") || return 1 if [ -n "$DIRNAME" ]; then FIRST_REL="$DELETED_BY/$DIRNAME/$BASENAME" SECOND_REL="$DELETED_BY/$DIRNAME/$COPY_NAME" + THIRD_REL="$DELETED_BY/$DIRNAME/$COPY2_NAME" else FIRST_REL="$DELETED_BY/$BASENAME" SECOND_REL="$DELETED_BY/$COPY_NAME" + THIRD_REL="$DELETED_BY/$COPY2_NAME" fi FIRST_RECYCLE=$(resolve_case_path "$VOLUME_ROOT" "$RECYCLE_REPOSITORY/$FIRST_REL") SECOND_RECYCLE=$(resolve_case_path "$VOLUME_ROOT" "$RECYCLE_REPOSITORY/$SECOND_REL") + THIRD_RECYCLE=$(resolve_case_path "$VOLUME_ROOT" "$RECYCLE_REPOSITORY/$THIRD_REL") FIRST_META=$(resolve_case_path "$VOLUME_ROOT" "$SALVAGE_REPOSITORY/$FIRST_REL.json") SECOND_META=$(resolve_case_path "$VOLUME_ROOT" "$SALVAGE_REPOSITORY/$SECOND_REL.json") -} - -clean_artifact() { - local artifact=$1 - if [ -e "$artifact" ]; then - emit "rm '$artifact'" - if rm -f -- "$artifact" 2>>"$REPORT_TMP"; then - : - else - emit "warning: could not remove stale test artifact: $artifact" - fi - else - emit "clean: $artifact" - fi + THIRD_META=$(resolve_case_path "$VOLUME_ROOT" "$SALVAGE_REPOSITORY/$THIRD_REL.json") } clean_stale_artifacts() { [ "$CLEAN_ARTIFACTS" -eq 1 ] || return 0 section "pre-clean stale salvage artifacts" - clean_artifact "$FIRST_META" - clean_artifact "$SECOND_META" - clean_artifact "$FIRST_RECYCLE" - clean_artifact "$SECOND_RECYCLE" + emit "local rm cleanup skipped; stale salvage entries are purged through NCP 87/18" } check_file() { @@ -272,30 +266,21 @@ cat_text_file() { return 0 } -write_live_payload() { - local label=$1 content=$2 +run_ncp_create_with_payload() { + local label=$1 content=$2 status section "$label" - emit "path=$UNIX_PATH" - emit "content=$content" - printf '%s\n' "$content" >"$UNIX_PATH" 2>>"$REPORT_TMP" || { - fail_check "could not write live payload: $UNIX_PATH" - return 1 - } - cat_text_file "$label: live file after write" "$UNIX_PATH" "$content" -} - -run_ncp_create_only() { - local label=$1 status - section "$label" - emit "\$ ./ncp_delete_smoke --create-only -S '$SERVER' -U '$USER_NAME' -P ****** '$NETWARE_PATH'" - "$SCRIPT_DIR/ncp_delete_smoke" --create-only -S "$SERVER" -U "$USER_NAME" \ - -P "$PASSWORD" "$NETWARE_PATH" 2>&1 | tee -a "$REPORT_TMP" + emit "payload=$content" + emit "\$ ./ncp_delete_smoke --create-only --payload '$content' -S '$SERVER' -U '$USER_NAME' -P ****** '$NETWARE_PATH'" + "$SCRIPT_DIR/ncp_delete_smoke" --create-only --payload "$content" \ + -S "$SERVER" -U "$USER_NAME" -P "$PASSWORD" "$NETWARE_PATH" \ + 2>&1 | tee -a "$REPORT_TMP" status=${PIPESTATUS[0]} emit "[exit=$status]" if [ "$status" -ne 0 ]; then - fail_check "NCP create-only failed for $NETWARE_PATH" + fail_check "NCP create/write failed for $NETWARE_PATH" return 1 fi + cat_text_file "$label: cat live file after NCP write" "$UNIX_PATH" "$content" return 0 } @@ -329,6 +314,13 @@ count_salvage_entries() { status=${PIPESTATUS[0]} emit "[exit=$status]" if [ "$status" -ne 0 ]; then + if grep -q "found no entries" "$scan_tmp" && grep -q "last_error=0x89ff" "$scan_tmp"; then + basename_count=0 + emit "NCP salvage scan matched 0 entries named $BASENAME" + printf -v "$out_var" '%s' "$basename_count" + rm -f "$scan_tmp" + return 0 + fi fail_check "NCP salvage scan failed for $SCAN_PATH" rm -f "$scan_tmp" return 1 @@ -364,8 +356,20 @@ run_ncp_salvage_purge_all() { return 0 } +salvage_paths_for_index() { + local index=$1 recycle_var=$2 meta_var=$3 recycle_path meta_path + case "$index" in + 0) recycle_path=$FIRST_RECYCLE; meta_path=$FIRST_META ;; + 1) recycle_path=$SECOND_RECYCLE; meta_path=$SECOND_META ;; + 2) recycle_path=$THIRD_RECYCLE; meta_path=$THIRD_META ;; + *) return 1 ;; + esac + printf -v "$recycle_var" '%s' "$recycle_path" + printf -v "$meta_var" '%s' "$meta_path" +} + recover_first_salvage_entry() { - local label=$1 expected_content=$2 before_count=0 after_count=0 status + local label=$1 before_count=0 after_count=0 status count_salvage_entries "$label: NCP salvage scan before recover" before_count || return 1 if [ "$before_count" -lt 1 ]; then @@ -387,7 +391,9 @@ recover_first_salvage_entry() { fi check_file "$label: restored live payload exists" "$UNIX_PATH" && \ - cat_text_file "$label: cat restored live file" "$UNIX_PATH" "$expected_content" + cat_text_file "$label: cat restored live file" "$UNIX_PATH" + grep -q "mars_nwe salvage payload cycle" "$UNIX_PATH" || \ + fail_check "$label: restored live file does not contain expected salvage payload marker" count_salvage_entries "$label: NCP salvage scan after recover" after_count || return 1 if [ "$after_count" -ne $((before_count - 1)) ]; then @@ -397,29 +403,38 @@ recover_first_salvage_entry() { return 0 } -run_recover_cycle() { - local label=$1 content=$2 +run_delete_capture_cycle() { + local index=$1 label=$2 content=$3 recycle_path meta_path - run_ncp_create_only "$label: NCP create live file" || return 1 - write_live_payload "$label: modify live file through Linux" "$content" || return 1 + salvage_paths_for_index "$index" recycle_path meta_path || { + fail_check "$label: unsupported version index $index" + return 1 + } + + run_ncp_create_with_payload "$label: NCP create/write live file" "$content" || return 1 run_ncp_delete_only "$label: NCP delete live file" || return 1 - check_file "$label: recycle payload before recover" "$FIRST_RECYCLE" && \ - cat_text_file "$label: cat recycle payload before recover" "$FIRST_RECYCLE" "$content" - check_file "$label: salvage metadata before recover" "$FIRST_META" && \ - cat_metadata "$label: cat salvage metadata before recover" "$FIRST_META" + check_file "$label: recycle payload before recover" "$recycle_path" && \ + cat_text_file "$label: cat recycle payload before recover" "$recycle_path" "$content" + check_file "$label: salvage metadata before recover" "$meta_path" && \ + cat_metadata "$label: cat salvage metadata before recover" "$meta_path" +} - recover_first_salvage_entry "$label" "$content" || return 1 +run_version_capture_cycles() { + local i label content count salvage_count=0 + i=0 + while [ "$i" -lt "$CYCLE_COUNT" ]; do + count=$((i + 1)) + label="version capture #$count" + content="mars_nwe salvage payload cycle $count" + run_delete_capture_cycle "$i" "$label" "$content" || return 1 + i=$((i + 1)) + done - if [ -e "$FIRST_RECYCLE" ]; then - fail_check "$label: recycle payload still exists after recover: $FIRST_RECYCLE" - else - emit "$label: recycle payload consumed: $FIRST_RECYCLE" - fi - if [ -e "$FIRST_META" ]; then - fail_check "$label: metadata sidecar still exists after recover: $FIRST_META" - else - emit "$label: metadata sidecar consumed: $FIRST_META" + count_salvage_entries "NCP salvage scan after version captures" salvage_count || return 1 + if [ "$salvage_count" -ne "$CYCLE_COUNT" ]; then + fail_check "expected $CYCLE_COUNT salvage entries after version captures, got $salvage_count" + return 1 fi } @@ -441,14 +456,17 @@ emit "deleted_by=$DELETED_BY" emit "recycle_repository=$RECYCLE_REPOSITORY" emit "salvage_repository=$SALVAGE_REPOSITORY" emit "clean_artifacts=$CLEAN_ARTIFACTS" +emit "cycles=$CYCLE_COUNT" if prepare_paths; then emit "volume_root=$VOLUME_ROOT" emit "relative_path=$RELATIVE_PATH" emit "first_recycle=$FIRST_RECYCLE" emit "second_recycle=$SECOND_RECYCLE" + emit "third_recycle=$THIRD_RECYCLE" emit "first_metadata=$FIRST_META" emit "second_metadata=$SECOND_META" + emit "third_metadata=$THIRD_META" emit "scan_path=$SCAN_PATH" clean_stale_artifacts else @@ -460,8 +478,8 @@ fi cleanup_live_file_if_present run_ncp_salvage_purge_all -run_recover_cycle "recover cycle #1" "mars_nwe salvage payload cycle 1" -run_recover_cycle "recover cycle #2" "mars_nwe salvage payload cycle 2 after recover" +run_version_capture_cycles +recover_first_salvage_entry "recover first version after $CYCLE_COUNT captures" finish_report [ "$FAILURES" -eq 0 ]