Merge remote-tracking branch 'origin/feat/existing' into dev

This commit is contained in:
2026-09-04 17:50:55 +02:00
parent 192aff8c46
commit c650f9a3d0
11 changed files with 123 additions and 70 deletions
+1
View File
@@ -229,6 +229,7 @@ static const OptionEntry OPTION_TABLE[] = {
{"--fsync", NULL, OPT_FLAG, offsetof(Config, use_fsync)},
{"--checksum", NULL, OPT_FLAG, offsetof(Config, checksum)},
{"--8-bit-output", "-8", OPT_FLAG, offsetof(Config, eight_bit_output)},
{"--existing", NULL, OPT_FLAG, offsetof(Config, existing)},
{"--source-dir", NULL, OPT_STRING, offsetof(Config, send_directory)},
{"--dest-dir", NULL, OPT_STRING, offsetof(Config, receive_root_directory)},
+1
View File
@@ -34,6 +34,7 @@ void print_usage(void) {
printf(" -I, --ignore-times Transfer files even when size and mtime match\n");
printf(" -@, --modify-window <sec> Modification time tolerance\n");
printf(" -u, --update Skip files newer than the source on receiver\n");
printf(" --existing Skip files not already present at destination\n");
printf(" --delta Delta transfer for changed files (requires --incremental)\n");
printf(" -W, --whole-file Transfer changed files without delta processing\n");
printf(" --delta-block <n> Delta block size in bytes (default: %d)\n",
+10 -6
View File
@@ -74,6 +74,7 @@ static void config_set_defaults(Config* config) {
config->list_only = false;
config->human_readable = false;
config->eight_bit_output = false;
config->existing = false;
config->update = false;
config->inplace = false;
config->use_fsync = false;
@@ -130,9 +131,10 @@ static bool validate_received_config(const Config* config) {
valid_wire_bool(config->safe_links) && valid_wire_bool(config->copy_unsafe_links) &&
valid_wire_bool(config->preserve_hard_links) && valid_wire_bool(config->preserve_acls) &&
valid_wire_bool(config->preserve_xattrs) && valid_wire_bool(config->preserve_devices) &&
valid_wire_bool(config->preserve_sparse) && valid_wire_bool(config->update) &&
valid_wire_bool(config->inplace) && valid_wire_bool(config->append) &&
valid_wire_bool(config->use_fsync) && valid_wire_bool(config->append_verify) &&
valid_wire_bool(config->preserve_sparse) && valid_wire_bool(config->existing) &&
valid_wire_bool(config->update) && valid_wire_bool(config->inplace) &&
valid_wire_bool(config->append) && valid_wire_bool(config->use_fsync) &&
valid_wire_bool(config->append_verify) &&
valid_wire_bool(config->delete_excluded) && valid_wire_bool(config->delete_after) &&
valid_wire_bool(config->relative) && valid_wire_bool(config->prune_empty_dirs) &&
valid_wire_bool(config->partial) && valid_wire_bool(config->delete_before) &&
@@ -255,8 +257,9 @@ static bool send_file_options(int fd, const Config* c) {
}
static bool send_selection_options(int fd, const Config* c) {
return send_int(fd, c->update) && send_int(fd, c->inplace) && send_int(fd, c->append) &&
send_int(fd, c->use_fsync) && send_int(fd, c->append_verify) &&
return send_int(fd, c->existing) && send_int(fd, c->update) && send_int(fd, c->inplace) &&
send_int(fd, c->append) && send_int(fd, c->use_fsync) &&
send_int(fd, c->append_verify) &&
send_int(fd, c->delete_excluded) && send_int(fd, c->delete_after) &&
send_n_data(fd, &c->max_delete, sizeof(c->max_delete)) && send_int(fd, c->relative) &&
send_int(fd, c->prune_empty_dirs);
@@ -325,7 +328,8 @@ static bool receive_file_options(int fd, Config* c) {
}
static bool receive_selection_options(int fd, Config* c) {
bool* flags[] = {&c->update, &c->inplace, &c->append, &c->use_fsync,
bool* flags[] = {&c->existing, &c->update, &c->inplace, &c->append,
&c->use_fsync,
&c->append_verify, &c->delete_excluded, &c->delete_after};
for (size_t i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
if (!receive_wire_bool(fd, flags[i]))
+1
View File
@@ -82,6 +82,7 @@ typedef struct Config {
bool eight_bit_output;
// Issue #127: Transfer modes
bool existing;
bool update;
bool inplace;
bool use_fsync;
+18 -1
View File
@@ -29,6 +29,7 @@ bool file_save_to_disk(const char* root_directory, const File* file, const Confi
const char* backup_dir = (config && config->backup_dir) ? config->backup_dir : NULL;
const char* partial_dir = (config && config->partial_dir) ? config->partial_dir : NULL;
char *confined_backup = NULL, *confined_partial = NULL, *disk_path = NULL;
char* destination_path = NULL;
char *backup_path = NULL, *parent_copy = NULL;
if (!file || !file->path || !file->data || (file->data->size != 0 && !file->data->data) ||
@@ -54,13 +55,27 @@ bool file_save_to_disk(const char* root_directory, const File* file, const Confi
const char* actual_root =
(partial_dir && config && config->partial) ? confined_partial : root_directory;
destination_path = path_cat(root_directory, file->path);
disk_path = path_cat(actual_root, file->path);
if (disk_path == NULL) {
if (destination_path == NULL || disk_path == NULL) {
free(confined_backup);
free(confined_partial);
free(destination_path);
free(disk_path);
return false;
}
/* --existing checks the final destination, not a temporary partial path. */
if (config && config->existing && !file_path_exists_secure(destination_path)) {
free(confined_backup);
free(confined_partial);
free(destination_path);
free(disk_path);
return true;
}
free(destination_path);
destination_path = NULL;
/* --update is receiver-side policy: never replace a newer destination.
The secure stat does not require read permission on the destination. */
if (config && config->update && file_destination_is_newer_secure(disk_path, file->metadata)) {
@@ -110,6 +125,7 @@ bool file_save_to_disk(const char* root_directory, const File* file, const Confi
free(backup_path);
free(confined_backup);
free(confined_partial);
free(destination_path);
free(disk_path);
return ok;
@@ -118,6 +134,7 @@ fail:
free(backup_path);
free(confined_backup);
free(confined_partial);
free(destination_path);
free(disk_path);
return false;
}