diff --git a/src/client/client_send.c b/src/client/client_send.c index df2c22f..0c33c45 100644 --- a/src/client/client_send.c +++ b/src/client/client_send.c @@ -597,14 +597,20 @@ static int send_delete_manifest(int fd, ArrayList* manifest) { --delete-before/--delete-during, where the extras are removed on the receiver BEFORE the first byte of file data is sent: the receiver acknowledges with STATUS_OK once the bounded delete committed, or STATUS_ERROR if it could not - (in which case the sender aborts without streaming any data). */ + (in which case the sender aborts without streaming any data). The ACK may + take much longer than an ordinary per-message round trip because the receiver + performs the whole bounded deletion walk (up to MAX_SERVER_DELETE_COUNT + unlinks) before replying, so the wait uses a generous explicit deadline + instead of the default 60 s receive window. */ +#define DELETE_ACK_TIMEOUT_SEC 3600 + static bool send_delete_manifest_early(Client* client, ArrayList* manifest) { if (!client || !manifest) return false; if (send_delete_manifest(client->file_descriptor, manifest) != 0) return false; Status ack; - if (!receive_status(client->file_descriptor, &ack)) + if (!receive_status_timed(client->file_descriptor, &ack, DELETE_ACK_TIMEOUT_SEC)) return false; if (ack != STATUS_OK) { log_message(LOG_LEVEL_ERROR, "Server failed to delete files before the transfer"); diff --git a/src/shared/protocol.c b/src/shared/protocol.c index 17caeb3..1d4a421 100644 --- a/src/shared/protocol.c +++ b/src/shared/protocol.c @@ -302,15 +302,25 @@ bool protocol_send_n_data(ProtocolSession* session, const void* data, size_t dat return true; } +bool protocol_receive_n_data_timed(ProtocolSession* session, void* data, size_t data_size, + int timeout_sec); + bool protocol_receive_n_data(ProtocolSession* session, void* data, size_t data_size) { + return protocol_receive_n_data_timed(session, data, data_size, RECEIVE_TIMEOUT_SEC); +} + +bool protocol_receive_n_data_timed(ProtocolSession* session, void* data, size_t data_size, + int timeout_sec) { log_debug_message(LOG_DEBUG_IO, " Receiving n Data: %zu", data_size); if (!session) return false; int fd = session->read_fd; + if (timeout_sec <= 0) + timeout_sec = RECEIVE_TIMEOUT_SEC; struct timespec deadline; clock_gettime(CLOCK_MONOTONIC, &deadline); - deadline.tv_sec += RECEIVE_TIMEOUT_SEC; + deadline.tv_sec += timeout_sec; size_t total_bytes_received = 0; short wait_events = POLLIN; @@ -319,7 +329,7 @@ bool protocol_receive_n_data(ProtocolSession* session, void* data, size_t data_s struct pollfd pfd = {.fd = fd, .events = wait_events}; int poll_result = poll(&pfd, 1, deadline_remaining_ms(&deadline)); if (poll_result == 0) { - log_message(LOG_LEVEL_ERROR, "Receive timeout after %ds", RECEIVE_TIMEOUT_SEC); + log_message(LOG_LEVEL_ERROR, "Receive timeout after %ds", timeout_sec); return false; } if (poll_result < 0) { @@ -516,6 +526,17 @@ bool protocol_receive_status(ProtocolSession* session, Status* status) { return true; } +/* protocol_receive_status with an explicit per-message deadline (seconds). + Used where a single reply may legitimately take far longer than the default + 60 s receive window - e.g. the sender waiting for the early-delete ACK after + the receiver committed a large (up to MAX_SERVER_DELETE_COUNT) deletion. */ +bool protocol_receive_status_timed(ProtocolSession* session, Status* status, int timeout_sec) { + if (!protocol_receive_n_data_timed(session, status, sizeof(Status), timeout_sec)) + return false; + log_debug_message(LOG_DEBUG_PROTO, "Received Status: %s", status_to_string(*status)); + return true; +} + bool send_str(int fd, const char* data) { return protocol_send_str(legacy_session(-1, fd), data); } @@ -543,3 +564,6 @@ bool send_status(int fd, Status status) { bool receive_status(int fd, Status* status) { return protocol_receive_status(legacy_session(fd, -1), status); } +bool receive_status_timed(int fd, Status* status, int timeout_sec) { + return protocol_receive_status_timed(legacy_session(fd, -1), status, timeout_sec); +} diff --git a/src/shared/protocol.h b/src/shared/protocol.h index d29d2fa..67a929f 100644 --- a/src/shared/protocol.h +++ b/src/shared/protocol.h @@ -112,5 +112,10 @@ bool send_int(int file_descriptor, int data); bool receive_int(int file_descriptor, int* data); bool send_status(int file_descriptor, Status status); bool receive_status(int file_descriptor, Status* status); +/* receive_status with an explicit per-message deadline in seconds, instead of + the default RECEIVE_TIMEOUT_SEC. A reply that may legitimately take longer + (e.g. the early-delete ACK after a large receiver-side deletion) must use + this so the sender does not abort after the deletion already committed. */ +bool receive_status_timed(int file_descriptor, Status* status, int timeout_sec); #endif