refactor(cli): move destination action policy to Rust
FIO_openDstFile retained the filesystem leaf in Rust but its C wrapper still encoded the status precedence for test mode, stdout, same-file protection, sparse-mode adjustment, overwrite prompting, removal, retry, and final errors. Centralize that pure action classification in Rust without moving FILE* handling, metadata, preference mutation, prompts, diagnostics, or retry callbacks across the ABI.\n\nThe action bridge validates status, confirmation, and sparse-state inputs and has focused tests for every destination outcome, sparse-first ordering, prompt acceptance/abort, quiet mode, overwrite mode, setvbuf/open failures, success, and invalid inputs.\n\nTest Plan:\n- ulimit -v 41943040; CARGO_BUILD_JOBS=1 cargo fmt/clippy gates passed before staging\n- git diff --cached --check\n- Full capped native and original-test verification follows after the batch is committed.
This commit is contained in:
+75
-36
@@ -366,6 +366,28 @@ enum {
|
||||
FIO_RUST_OPEN_DST_NULL_DEVICE_REGULAR = 6,
|
||||
FIO_RUST_OPEN_DST_OPEN_FAILED = 7,
|
||||
};
|
||||
enum {
|
||||
FIO_RUST_OPEN_DST_CONFIRMATION_UNASKED = -1,
|
||||
FIO_RUST_OPEN_DST_CONFIRMATION_ACCEPT = 0,
|
||||
FIO_RUST_OPEN_DST_CONFIRMATION_ABORT = 1,
|
||||
};
|
||||
enum {
|
||||
FIO_RUST_OPEN_DST_ACTION_TEST_MODE = 0,
|
||||
FIO_RUST_OPEN_DST_ACTION_STDOUT = 1,
|
||||
FIO_RUST_OPEN_DST_ACTION_SAME_FILE = 2,
|
||||
FIO_RUST_OPEN_DST_ACTION_ADJUST_SPARSE = 3,
|
||||
FIO_RUST_OPEN_DST_ACTION_NULL_DEVICE_REGULAR = 4,
|
||||
FIO_RUST_OPEN_DST_ACTION_EXISTING_QUIET_ABORT = 5,
|
||||
FIO_RUST_OPEN_DST_ACTION_EXISTING_PROMPT = 6,
|
||||
FIO_RUST_OPEN_DST_ACTION_EXISTING_ABORT = 7,
|
||||
FIO_RUST_OPEN_DST_ACTION_EXISTING_REMOVE = 8,
|
||||
FIO_RUST_OPEN_DST_ACTION_OPEN_FAILED = 9,
|
||||
FIO_RUST_OPEN_DST_ACTION_SETBUF_FAILED = 10,
|
||||
FIO_RUST_OPEN_DST_ACTION_SUCCESS = 11,
|
||||
FIO_RUST_OPEN_DST_ACTION_INVALID = 12,
|
||||
};
|
||||
int FIO_rust_openDstFileAction(int status, int overwrite, int displayLevel,
|
||||
int confirmation, int sparseAdjusted);
|
||||
int FIO_rust_openDstFile(int testMode,
|
||||
int allowExisting,
|
||||
const char* srcFileName,
|
||||
@@ -690,18 +712,24 @@ FIO_openDstFile(FIO_ctx_t* fCtx, FIO_prefs_t* const prefs,
|
||||
int isDstRegFile = 0;
|
||||
int allowExisting = 0;
|
||||
int sparseAdjusted = 0;
|
||||
int confirmation = FIO_RUST_OPEN_DST_CONFIRMATION_UNASKED;
|
||||
int status;
|
||||
FILE* f = NULL;
|
||||
|
||||
status = FIO_rust_openDstFile(prefs->testMode, allowExisting,
|
||||
srcFileName, dstFileName, mode,
|
||||
&isDstRegFile, &f);
|
||||
if (status == FIO_RUST_OPEN_DST_TEST_MODE)
|
||||
return NULL; /* do not open file in test mode */
|
||||
|
||||
assert(dstFileName != NULL);
|
||||
for (;;) {
|
||||
if (status == FIO_RUST_OPEN_DST_STDOUT) {
|
||||
int const action = FIO_rust_openDstFileAction(
|
||||
status, prefs->overwrite, g_display_prefs.displayLevel,
|
||||
confirmation, sparseAdjusted);
|
||||
|
||||
if (action == FIO_RUST_OPEN_DST_ACTION_TEST_MODE)
|
||||
return NULL; /* do not open file in test mode */
|
||||
|
||||
assert(dstFileName != NULL);
|
||||
switch (action) {
|
||||
case FIO_RUST_OPEN_DST_ACTION_STDOUT:
|
||||
DISPLAYLEVEL(4,"Using stdout for output \n");
|
||||
SET_BINARY_MODE(stdout);
|
||||
if (prefs->sparseFileSupport == 1) {
|
||||
@@ -709,18 +737,15 @@ FIO_openDstFile(FIO_ctx_t* fCtx, FIO_prefs_t* const prefs,
|
||||
DISPLAYLEVEL(4, "Sparse File Support is automatically disabled on stdout ; try --sparse \n");
|
||||
}
|
||||
return stdout;
|
||||
}
|
||||
|
||||
if (status == FIO_RUST_OPEN_DST_SAME_FILE) {
|
||||
case FIO_RUST_OPEN_DST_ACTION_SAME_FILE:
|
||||
DISPLAYLEVEL(1, "zstd: Refusing to open an output file which will overwrite the input file \n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Keep the C-owned preference mutation and its diagnostics here. Do
|
||||
* this only for the first classification: after an existing file is
|
||||
* removed, the second Rust call must not reclassify it as a missing
|
||||
* destination and emit a different sparse-mode message. */
|
||||
if (!sparseAdjusted) {
|
||||
case FIO_RUST_OPEN_DST_ACTION_ADJUST_SPARSE:
|
||||
/* Rust orders this once-only policy action before all ordinary
|
||||
* destination outcomes. Keep the preference mutation and its
|
||||
* diagnostic in C, and let the next loop iteration classify the
|
||||
* same status again. */
|
||||
if (prefs->sparseFileSupport == 1) {
|
||||
prefs->sparseFileSupport = ZSTD_SPARSE_DEFAULT;
|
||||
if (!isDstRegFile) {
|
||||
@@ -729,47 +754,61 @@ FIO_openDstFile(FIO_ctx_t* fCtx, FIO_prefs_t* const prefs,
|
||||
}
|
||||
}
|
||||
sparseAdjusted = 1;
|
||||
}
|
||||
continue;
|
||||
|
||||
if (status == FIO_RUST_OPEN_DST_NULL_DEVICE_REGULAR) {
|
||||
case FIO_RUST_OPEN_DST_ACTION_NULL_DEVICE_REGULAR:
|
||||
EXM_THROW(40, "%s is unexpectedly categorized as a regular file",
|
||||
dstFileName);
|
||||
}
|
||||
return NULL;
|
||||
|
||||
if (status == FIO_RUST_OPEN_DST_EXISTING) {
|
||||
if (!prefs->overwrite) {
|
||||
if (g_display_prefs.displayLevel <= 1) {
|
||||
/* No interaction possible */
|
||||
DISPLAYLEVEL(1, "zstd: %s already exists; not overwritten \n",
|
||||
dstFileName);
|
||||
return NULL;
|
||||
}
|
||||
DISPLAY("zstd: %s already exists; ", dstFileName);
|
||||
if (UTIL_requireUserConfirmation("overwrite (y/n) ? ", "Not overwritten \n", "yY", fCtx->hasStdinInput))
|
||||
return NULL;
|
||||
}
|
||||
case FIO_RUST_OPEN_DST_ACTION_EXISTING_QUIET_ABORT:
|
||||
/* No interaction possible. */
|
||||
DISPLAYLEVEL(1, "zstd: %s already exists; not overwritten \n",
|
||||
dstFileName);
|
||||
return NULL;
|
||||
|
||||
case FIO_RUST_OPEN_DST_ACTION_EXISTING_PROMPT:
|
||||
DISPLAY("zstd: %s already exists; ", dstFileName);
|
||||
confirmation = UTIL_requireUserConfirmation(
|
||||
"overwrite (y/n) ? ", "Not overwritten \n", "yY",
|
||||
fCtx->hasStdinInput)
|
||||
? FIO_RUST_OPEN_DST_CONFIRMATION_ABORT
|
||||
: FIO_RUST_OPEN_DST_CONFIRMATION_ACCEPT;
|
||||
continue;
|
||||
|
||||
case FIO_RUST_OPEN_DST_ACTION_EXISTING_ABORT:
|
||||
/* UTIL_requireUserConfirmation() already emitted the exact
|
||||
* existing diagnostic before returning the abort result. */
|
||||
return NULL;
|
||||
|
||||
case FIO_RUST_OPEN_DST_ACTION_EXISTING_REMOVE:
|
||||
/* Keep the existing C wrapper so its stat/non-regular diagnostics
|
||||
* remain unchanged. The next Rust call opens with O_TRUNC. */
|
||||
FIO_removeFile(dstFileName);
|
||||
allowExisting = 1;
|
||||
f = NULL;
|
||||
confirmation = FIO_RUST_OPEN_DST_CONFIRMATION_UNASKED;
|
||||
status = FIO_rust_openDstFile(prefs->testMode, allowExisting,
|
||||
srcFileName, dstFileName, mode,
|
||||
&isDstRegFile, &f);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (status == FIO_RUST_OPEN_DST_OPEN_FAILED) {
|
||||
case FIO_RUST_OPEN_DST_ACTION_OPEN_FAILED:
|
||||
DISPLAYLEVEL(1, "zstd: %s: %s\n", dstFileName, strerror(errno));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (status == FIO_RUST_OPEN_DST_SETBUF_FAILED)
|
||||
case FIO_RUST_OPEN_DST_ACTION_SETBUF_FAILED:
|
||||
DISPLAYLEVEL(2, "Warning: setvbuf failed for %s\n", dstFileName);
|
||||
return f;
|
||||
|
||||
assert(status == FIO_RUST_OPEN_DST_SUCCESS
|
||||
|| status == FIO_RUST_OPEN_DST_SETBUF_FAILED);
|
||||
return f;
|
||||
case FIO_RUST_OPEN_DST_ACTION_SUCCESS:
|
||||
return f;
|
||||
|
||||
case FIO_RUST_OPEN_DST_ACTION_INVALID:
|
||||
default:
|
||||
assert(0);
|
||||
return f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user