feat: add rsync-compatible fsync
CI / lint (pull_request) Successful in 12s
CI / sanitizers (address) (pull_request) Successful in 40s
CI / sanitizers (undefined) (pull_request) Successful in 38s
CI / fuzz-build (pull_request) Successful in 14s
CI / coverage (pull_request) Successful in 31s
CI / build-and-test (pull_request) Successful in 1m15s
CI / valgrind (pull_request) Successful in 33s

This commit is contained in:
2026-09-03 18:28:02 +02:00
parent 190fc5d300
commit 4982e15969
11 changed files with 60 additions and 14 deletions
+11 -8
View File
@@ -70,6 +70,7 @@ static void config_set_defaults(Config* config) {
config->human_readable = false;
config->update = false;
config->inplace = false;
config->use_fsync = false;
config->append = false;
config->append_verify = false;
config->delete_excluded = false;
@@ -125,10 +126,11 @@ static bool validate_received_config(const Config* config) {
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->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) && valid_wire_bool(config->checksum) &&
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) &&
valid_wire_bool(config->checksum) &&
(!config->use_compression ||
(config->compression_level >= 1 && config->compression_level <= 22)) &&
config->chunk_size > 0 && config->chunk_size <= MAX_CHUNK_SIZE &&
@@ -244,9 +246,10 @@ 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->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);
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);
}
static bool send_resume_options(int fd, const Config* c) {
@@ -304,7 +307,7 @@ 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,
bool* flags[] = {&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]))
+2 -1
View File
@@ -78,6 +78,7 @@ typedef struct Config {
// Issue #127: Transfer modes
bool update;
bool inplace;
bool use_fsync;
bool append;
bool append_verify;
@@ -128,7 +129,7 @@ typedef struct Config {
char* compress_choice;
} Config;
#define PROTOCOL_VERSION "2.2.0"
#define PROTOCOL_VERSION "2.3.0"
#define DEFAULT_CHUNK_SIZE (10 * 1024 * 1024)
Config* config_create(void);
+12 -2
View File
@@ -302,8 +302,9 @@ bool file_rename_secure(const char* old_path, const char* new_path) {
return ok;
}
bool file_to_disk_secure(const char* path, const void* data, unsigned long long data_size,
bool inplace, bool sparse, const FileMetadata* metadata) {
bool file_to_disk_secure_with_fsync(const char* path, const void* data,
unsigned long long data_size, bool inplace, bool sparse,
const FileMetadata* metadata, bool use_fsync) {
char* leaf = NULL;
int dirfd = file_open_secure_parent(path, &leaf, true);
if (dirfd < 0)
@@ -317,6 +318,8 @@ bool file_to_disk_secure(const char* path, const void* data, unsigned long long
ok = write_all(fd, data, data_size);
if (ok && metadata)
ok = file_restore_metadata_fd(fd, metadata);
if (ok && use_fsync)
ok = fsync(fd) == 0;
}
} else {
char tmp[NAME_MAX];
@@ -331,6 +334,8 @@ bool file_to_disk_secure(const char* path, const void* data, unsigned long long
ok = write_all(fd, data, data_size);
if (ok && metadata)
ok = file_restore_metadata_fd(fd, metadata);
if (ok && use_fsync)
ok = fsync(fd) == 0;
if (close(fd) != 0)
ok = false;
fd = -1;
@@ -347,6 +352,11 @@ bool file_to_disk_secure(const char* path, const void* data, unsigned long long
return ok;
}
bool file_to_disk_secure(const char* path, const void* data, unsigned long long data_size,
bool inplace, bool sparse, const FileMetadata* metadata) {
return file_to_disk_secure_with_fsync(path, data, data_size, inplace, sparse, metadata, false);
}
bool file_write_to_disk(const char* path, const void* data, unsigned long long data_size,
bool inplace, bool sparse) {
if (!path || (!data && data_size != 0) || has_path_traversal(path))
+3
View File
@@ -32,5 +32,8 @@ bool file_ensure_directory_secure(const char* path);
bool file_rename_secure(const char* old_path, const char* new_path);
bool file_to_disk_secure(const char* path, const void* data, unsigned long long data_size,
bool inplace, bool sparse, const FileMetadata* metadata);
bool file_to_disk_secure_with_fsync(const char* path, const void* data,
unsigned long long data_size, bool inplace, bool sparse,
const FileMetadata* metadata, bool use_fsync);
#endif
+2 -2
View File
@@ -103,8 +103,8 @@ bool file_save_to_disk(const char* root_directory, const File* file, const Confi
}
}
bool ok = file_to_disk_secure(disk_path, file->data->data, file->data->size, inplace, sparse,
file->metadata);
bool ok = file_to_disk_secure_with_fsync(disk_path, file->data->data, file->data->size, inplace,
sparse, file->metadata, config && config->use_fsync);
free(parent_copy);
free(backup_path);
free(confined_backup);