fix(p6-stop): block delete-manifest on early stop; sync -m manifest access; overflow guard
This commit is contained in:
+52
-26
@@ -1403,6 +1403,7 @@ static int send_chunks_multithreaded(void* pipeline_context) {
|
||||
if (stop_condition_reached(&context->stop_condition)) {
|
||||
log_info_message(LOG_INFO_MISC,
|
||||
"Stop deadline reached; stopping transfer at the next chunk boundary");
|
||||
context->scan_stopped_early = true;
|
||||
pipeline_cancel(context);
|
||||
break;
|
||||
}
|
||||
@@ -1446,9 +1447,19 @@ static int send_chunks_multithreaded(void* pipeline_context) {
|
||||
}
|
||||
|
||||
/* Completion tail: reached on natural exhaustion or an early stop deadline.
|
||||
The keep-set manifest is committed exactly where it normally would be, so
|
||||
--delete (delete-after timing) still commits its extras deletion. */
|
||||
if (context->config->use_delete && !context->early_delete) {
|
||||
A deadline that cut the scan short leaves an incomplete keep-set manifest;
|
||||
transmitting it would make the receiver --delete the unscanned source
|
||||
mirrors (data loss), so it is deliberately suppressed. Suppressing it also
|
||||
means the manifest (which the scanner thread may still be appending) is
|
||||
never read here on the early-stop path, so no scanner synchronization is
|
||||
required to enter the tail. */
|
||||
context->scan_stopped_early =
|
||||
context->scan_stopped_early || stop_condition_reached(&context->stop_condition);
|
||||
if (context->scan_stopped_early) {
|
||||
log_message(LOG_LEVEL_WARNING,
|
||||
"transfer stopped early (stop deadline); skipping --delete keep-set so "
|
||||
"unscanned source mirrors are not deleted");
|
||||
} else if (context->config->use_delete && !context->early_delete) {
|
||||
/* Empty keep-set + scan I/O error must not delete the whole destination
|
||||
(the source may not be genuinely empty -- see send_files). */
|
||||
bool empty_io;
|
||||
@@ -1826,15 +1837,18 @@ int send_files(Config* config) {
|
||||
int total_files = 0;
|
||||
time_t last_progress = 0;
|
||||
time_t start = time(NULL);
|
||||
/* True when the stop deadline cut the scan short so the keep-set manifest is
|
||||
only a prefix of the source. */
|
||||
bool scan_stopped_early = false;
|
||||
while ((current_chunk = directory_scanner_next(scanner)) != NULL) {
|
||||
/* Phase 6: stop-elegantly at the next chunk boundary once the deadline has
|
||||
passed. The scanner may also have stopped early itself; either way the
|
||||
completion tail below keeps everything already sent and still commits a
|
||||
--delete keep-set manifest. */
|
||||
completion tail below keeps everything already sent. */
|
||||
if (stop_condition_reached(&stop)) {
|
||||
chunk_destroy(current_chunk);
|
||||
log_info_message(LOG_INFO_MISC,
|
||||
"Stop deadline reached; stopping transfer at the next chunk boundary");
|
||||
scan_stopped_early = true;
|
||||
break;
|
||||
}
|
||||
unsigned long long chunk_bytes = 0;
|
||||
@@ -1890,31 +1904,43 @@ int send_files(Config* config) {
|
||||
goto send_fail;
|
||||
if (directory_scanner_had_io_error(scanner))
|
||||
had_scan_io = true;
|
||||
if (had_scan_io && manifest && manifest->size == 0) {
|
||||
/* A scan that hit an I/O error and produced no keep entries is ambiguous;
|
||||
an empty keep-set would delete the whole destination. Refuse to delete
|
||||
(see the early-timing comment above). */
|
||||
log_message(LOG_LEVEL_ERROR,
|
||||
"source scan hit an I/O error before finding any file; refusing to delete with "
|
||||
"an empty keep-set (--delete)");
|
||||
goto send_fail;
|
||||
}
|
||||
if ((manifest || config->delete_missing_args) && !delete_early) {
|
||||
/* Late (commit) ordering: all file data is out; transmit the manifest so
|
||||
the receiver commits the extras walk (--delete) and/or the
|
||||
--delete-missing-args exact-path deletions only after the transfer
|
||||
succeeds. In the early modes (--delete-before/--delete-during) the
|
||||
manifest already went out up front, so nothing is re-sent here. */
|
||||
if (send_delete_manifest(client->file_descriptor, manifest, excluded, missing_args) != 0) {
|
||||
/* Phase 6: the scanner may have stopped early (returning NULL without a
|
||||
failure) as soon as the deadline passed, so reflect that here too. A
|
||||
deadline that cut the scan short leaves an incomplete keep-set; transmitting
|
||||
it would make the receiver --delete the unscanned source mirrors (data
|
||||
loss), so the late delete manifest is suppressed below. */
|
||||
scan_stopped_early = scan_stopped_early || stop_condition_reached(&stop);
|
||||
if (scan_stopped_early) {
|
||||
log_message(LOG_LEVEL_WARNING,
|
||||
"transfer stopped early (stop deadline); skipping --delete keep-set so "
|
||||
"unscanned source mirrors are not deleted");
|
||||
} else {
|
||||
if (had_scan_io && manifest && manifest->size == 0) {
|
||||
/* A scan that hit an I/O error and produced no keep entries is ambiguous;
|
||||
an empty keep-set would delete the whole destination. Refuse to delete
|
||||
(see the early-timing comment above). */
|
||||
log_message(LOG_LEVEL_ERROR,
|
||||
"source scan hit an I/O error before finding any file; refusing to delete with "
|
||||
"an empty keep-set (--delete)");
|
||||
goto send_fail;
|
||||
}
|
||||
if ((manifest || config->delete_missing_args) && !delete_early) {
|
||||
/* Late (commit) ordering: all file data is out; transmit the manifest so
|
||||
the receiver commits the extras walk (--delete) and/or the
|
||||
--delete-missing-args exact-path deletions only after the transfer
|
||||
succeeds. In the early modes (--delete-before/--delete-during) the
|
||||
manifest already went out up front, so nothing is re-sent here. */
|
||||
if (send_delete_manifest(client->file_descriptor, manifest, excluded, missing_args) != 0) {
|
||||
if (manifest) {
|
||||
array_list_delete(manifest);
|
||||
manifest = NULL;
|
||||
}
|
||||
goto send_fail;
|
||||
}
|
||||
if (manifest) {
|
||||
array_list_delete(manifest);
|
||||
manifest = NULL;
|
||||
}
|
||||
goto send_fail;
|
||||
}
|
||||
if (manifest) {
|
||||
array_list_delete(manifest);
|
||||
manifest = NULL;
|
||||
}
|
||||
}
|
||||
bool ok = finalize_transfer(client, config, remove_sources);
|
||||
|
||||
Reference in New Issue
Block a user