CI / lint (pull_request) Successful in 26s
CI / sanitizers (address) (pull_request) Failing after 41s
CI / sanitizers (undefined) (pull_request) Successful in 40s
CI / fuzz-build (pull_request) Successful in 18s
CI / coverage (pull_request) Successful in 35s
CI / valgrind (pull_request) Failing after 37s
CI / build-and-test (pull_request) Failing after 2m35s
RSYNC_COMPAT Phase-2 row M: path-list construction and destination directory creation while preserving traversal safety. - -R/--relative with --files-from: transmit each listed entry under its bare relative destination path (no source-root mirror). Files keep an absolute local read path plus a separate wire/dest path (File.send_path); manifest, incremental quick-check and change output follow the wire path, so --delete and --remove-source-files stay consistent. -R without --files-from is unchanged (full mirror). - --no-implied-dirs: client-only, only meaningful with -R + --files-from. A listed file whose parent dir is not itself (or via an ancestor) explicitly listed cannot be placed; the run fails up front with a clear error. No effect otherwise. - --dirs/-d + --old-dirs/--old-d aliases: -d <dir> transmits the source root as an explicit empty directory entry (STATUS_MKDIR frame); with --files-from listed dirs are created empty and listed files transferred, never descending. Works single-threaded, -m (sequential scanner in the -m scan thread) and chunk-serialization (per-file type marker). Directory entries appear in the delete manifest. - --mkpath: new wire bool; server creates the destination root (and missing leading components under its authorized root) at connection start. A missing destination root is now rejected by default. - Protocol bumped to 2.7.0 (mkpath wire field + STATUS_MKDIR + chunk type marker). All receive paths funnel through file_save_to_disk_full which creates directories via the secure confined mkdir engine; dir entries are excluded from --remove-source-files outcome acknowledgements on both ends. - Unit coverage: CLI parse (relative/dirs aliases/mkpath/no-implied-dirs), config round-trip (relative + mkpath), scanner --dirs non-recursion and -R send_path (sequential + parallel), receiver dir-entry save. - Integration coverage: TestRelativeFilesFrom, TestNoImpliedDirs, TestDirs, TestMkpath (single and -m). - RSYNC_COMPAT: 4 rows move to Implemented (Summary 67/3/5/1/71 = 147).
117 lines
4.7 KiB
C
117 lines
4.7 KiB
C
#ifndef PROTOCOL_H
|
|
#define PROTOCOL_H
|
|
|
|
#include "data.h"
|
|
#include <stdbool.h>
|
|
#include <stddef.h>
|
|
#include <stdatomic.h>
|
|
|
|
/* Maximum allowed string size for receive_str (64 KB) */
|
|
#define MAX_STRING_SIZE (64 * 1024)
|
|
|
|
/* Maximum uncompressed file payload accepted by the receiver's whole-file
|
|
* paths. A single whole file is charged against the per-connection memory
|
|
* reservation (MAX_CONNECTION_MEMORY) and against the server allocation
|
|
* ceiling (MAX_SERVER_ALLOC), so this mirrors those 256 MB bounds rather than
|
|
* the older 64 MB chunk-era cap. Chunk-serialized payloads keep their own
|
|
* 64 MB cap (MAX_CHUNK_SIZE). */
|
|
#define MAX_RECEIVE_WHOLE_FILE_SIZE (256ULL * 1024 * 1024)
|
|
|
|
/* Maximum allowed data payload size for receive_data (whole-file bound) */
|
|
#define MAX_DATA_PAYLOAD_SIZE MAX_RECEIVE_WHOLE_FILE_SIZE
|
|
|
|
/* Maximum chunk size (64 MB) — prevents unbounded allocation from the wire */
|
|
#define MAX_CHUNK_SIZE (64ULL * 1024 * 1024)
|
|
#define MAX_MANIFEST_ENTRIES (1024 * 1024)
|
|
/* Aggregate bytes retained by one received deletion manifest. */
|
|
#define MAX_MANIFEST_BYTES (16ULL * 1024 * 1024)
|
|
#define DEFAULT_MAX_ALLOC (1ULL * 1024 * 1024 * 1024)
|
|
/* Server policy ceiling for a client-provided allocation limit. */
|
|
#define MAX_SERVER_ALLOC (256ULL * 1024 * 1024)
|
|
/* Bounded cumulative per-connection receive budget. In-flight wire buffers,
|
|
decompression buffers and queued (not yet written) file payloads for a
|
|
connection must stay within this ceiling. */
|
|
#define MAX_CONNECTION_MEMORY (256ULL * 1024 * 1024)
|
|
|
|
typedef struct ssl_st SSL;
|
|
|
|
/*
|
|
* Explicit owner of protocol I/O. A session does not own the descriptors or
|
|
* SSL object; it only describes the transport used by a transfer. This makes
|
|
* it safe to pass the transport to a worker without relying on inherited
|
|
* thread-local state.
|
|
*/
|
|
typedef struct ProtocolSession {
|
|
int read_fd;
|
|
int write_fd;
|
|
SSL* ssl;
|
|
unsigned long long bwlimit;
|
|
long long bw_tokens;
|
|
long long bw_last_refill_sec;
|
|
long bw_last_refill_nsec;
|
|
atomic_ullong total_allocated_bytes;
|
|
bool eight_bit_output;
|
|
unsigned long long max_alloc;
|
|
} ProtocolSession;
|
|
|
|
typedef int Status;
|
|
enum NET_STATUS {
|
|
STATUS_OK,
|
|
STATUS_ERROR,
|
|
STATUS_FINISHED,
|
|
STATUS_NEXT,
|
|
STATUS_CHUNK,
|
|
STATUS_MANIFEST,
|
|
STATUS_CHECK,
|
|
STATUS_DELTA_SIGNATURE,
|
|
STATUS_DELTA_DATA,
|
|
STATUS_KEEPALIVE,
|
|
STATUS_ABORT,
|
|
STATUS_CHECK_BATCH,
|
|
/* An explicit directory entry (--dirs): the sender transmits only the path;
|
|
* the receiver creates the directory below the receive root. */
|
|
STATUS_MKDIR
|
|
};
|
|
|
|
void io_set_fds(int read_fd, int write_fd);
|
|
void io_set_bwlimit(unsigned long long bytes_per_sec);
|
|
void io_set_ssl(SSL* ssl);
|
|
SSL* io_get_ssl(void);
|
|
|
|
void protocol_session_init(ProtocolSession* session, int read_fd, int write_fd);
|
|
/* Transitional bridge for helpers whose signatures still carry only an fd. */
|
|
void protocol_session_bind(ProtocolSession* session);
|
|
void protocol_session_unbind(void);
|
|
void protocol_session_set_ssl(ProtocolSession* session, SSL* ssl);
|
|
void protocol_session_set_bwlimit(ProtocolSession* session, unsigned long long bytes_per_sec);
|
|
void protocol_session_set_max_alloc(ProtocolSession* session, unsigned long long max_alloc);
|
|
void* protocol_alloc(size_t size);
|
|
void* protocol_realloc(void* ptr, size_t size);
|
|
void protocol_session_set_8_bit_output(ProtocolSession* session, bool enabled);
|
|
void protocol_set_8_bit_output(bool enabled);
|
|
bool protocol_send_n_data(ProtocolSession* session, const void* data, size_t data_size);
|
|
bool protocol_receive_n_data(ProtocolSession* session, void* data, size_t data_size);
|
|
bool protocol_send_str(ProtocolSession* session, const char* data);
|
|
char* protocol_receive_str(ProtocolSession* session);
|
|
bool protocol_send_data(ProtocolSession* session, const Data* data);
|
|
Data* protocol_receive_data(ProtocolSession* session);
|
|
Data* protocol_receive_data_limited(ProtocolSession* session, unsigned long long maximum_size);
|
|
bool protocol_send_int(ProtocolSession* session, int data);
|
|
bool protocol_receive_int(ProtocolSession* session, int* data);
|
|
bool protocol_send_status(ProtocolSession* session, Status status);
|
|
bool protocol_receive_status(ProtocolSession* session, Status* status);
|
|
bool send_n_data(int file_descriptor, const void* data, size_t data_size);
|
|
bool receive_n_data(int file_descriptor, void* data, size_t data_size);
|
|
|
|
bool send_str(int file_descriptor, const char* data);
|
|
char* receive_str(int file_descriptor);
|
|
bool send_data(int file_descriptor, const Data* data);
|
|
Data* receive_data(int file_descriptor);
|
|
Data* receive_data_limited(int file_descriptor, unsigned long long maximum_size);
|
|
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);
|
|
|
|
#endif
|