fix: complete ignore-existing destination protection
CI / lint (pull_request) Successful in 12s
CI / sanitizers (undefined) (pull_request) Successful in 38s
CI / sanitizers (address) (pull_request) Successful in 38s
CI / fuzz-build (pull_request) Successful in 14s
CI / coverage (pull_request) Successful in 32s
CI / build-and-test (pull_request) Successful in 1m15s
CI / valgrind (pull_request) Successful in 34s
CI / lint (pull_request) Successful in 12s
CI / sanitizers (undefined) (pull_request) Successful in 38s
CI / sanitizers (address) (pull_request) Successful in 38s
CI / fuzz-build (pull_request) Successful in 14s
CI / coverage (pull_request) Successful in 32s
CI / build-and-test (pull_request) Successful in 1m15s
CI / valgrind (pull_request) Successful in 34s
This commit is contained in:
@@ -465,7 +465,7 @@ defaults to the current directory. |
|
|||||||
|
|
||||||
## Protocol and Security
|
## Protocol and Security
|
||||||
|
|
||||||
FastSync protocol version `2.2.0` is shared by the client and server. The
|
FastSync protocol version `2.3.0` is shared by the client and server. The
|
||||||
current protocol is sender-driven and includes configuration negotiation,
|
current protocol is sender-driven and includes configuration negotiation,
|
||||||
incremental checks, checksums, manifests, keep-alives, abort handling, and
|
incremental checks, checksums, manifests, keep-alives, abort handling, and
|
||||||
FastSync-native delta messages. Client and server versions must currently
|
FastSync-native delta messages. Client and server versions must currently
|
||||||
|
|||||||
@@ -22,7 +22,9 @@
|
|||||||
#define MAX_FILE_DATA_SIZE MAX_RECEIVE_FILE_SIZE
|
#define MAX_FILE_DATA_SIZE MAX_RECEIVE_FILE_SIZE
|
||||||
|
|
||||||
bool file_save_to_disk(const char* root_directory, const File* file, const Config* config) {
|
bool file_save_to_disk(const char* root_directory, const File* file, const Config* config) {
|
||||||
bool backup_enabled = config && config->backup;
|
/* Backups are incompatible with ignore-existing: moving the entry first
|
||||||
|
would make a concurrent no-replace commit overwrite its old name. */
|
||||||
|
bool backup_enabled = config && config->backup && !config->ignore_existing;
|
||||||
bool inplace = config && config->inplace;
|
bool inplace = config && config->inplace;
|
||||||
bool sparse = config && config->preserve_sparse;
|
bool sparse = config && config->preserve_sparse;
|
||||||
const char* backup_suffix = (config && config->suffix) ? config->suffix : "~";
|
const char* backup_suffix = (config && config->suffix) ? config->suffix : "~";
|
||||||
@@ -64,9 +66,8 @@ bool file_save_to_disk(const char* root_directory, const File* file, const Confi
|
|||||||
/* --ignore-existing checks the final destination before partial files or
|
/* --ignore-existing checks the final destination before partial files or
|
||||||
overwrite policies can modify it. */
|
overwrite policies can modify it. */
|
||||||
if (config && config->ignore_existing) {
|
if (config && config->ignore_existing) {
|
||||||
struct stat destination_stat;
|
|
||||||
char* final_path = path_cat(root_directory, file->path);
|
char* final_path = path_cat(root_directory, file->path);
|
||||||
bool exists = final_path && file_stat_secure(final_path, &destination_stat);
|
bool exists = final_path && file_path_exists_secure(final_path);
|
||||||
free(final_path);
|
free(final_path);
|
||||||
if (exists) {
|
if (exists) {
|
||||||
free(confined_backup);
|
free(confined_backup);
|
||||||
|
|||||||
@@ -120,6 +120,67 @@ static void test_file_save_to_disk_ignore_existing() {
|
|||||||
rmdir("test_ignore_existing_tmp");
|
rmdir("test_ignore_existing_tmp");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void test_file_save_to_disk_ignore_existing_entry_types() {
|
||||||
|
const char* root = "test_ignore_existing_entries_tmp";
|
||||||
|
const char* directory = "test_ignore_existing_entries_tmp/directory";
|
||||||
|
const char* link = "test_ignore_existing_entries_tmp/link";
|
||||||
|
const char* target = "test_ignore_existing_entries_tmp/target";
|
||||||
|
const char* backup = "test_ignore_existing_entries_tmp/backup.txt~";
|
||||||
|
const char* backup_file = "test_ignore_existing_entries_tmp/backup.txt";
|
||||||
|
Config* config = config_create();
|
||||||
|
File* file = file_create("unused");
|
||||||
|
|
||||||
|
unlink(link);
|
||||||
|
unlink(target);
|
||||||
|
unlink(backup);
|
||||||
|
unlink(backup_file);
|
||||||
|
rmdir(directory);
|
||||||
|
rmdir(root);
|
||||||
|
EXPECT_NOT_NULL(config);
|
||||||
|
EXPECT_NOT_NULL(file);
|
||||||
|
// cppcheck-suppress knownConditionTrueFalse
|
||||||
|
if (!config || !file)
|
||||||
|
return;
|
||||||
|
config->ignore_existing = true;
|
||||||
|
config->backup = true;
|
||||||
|
file->data->data = malloc(3);
|
||||||
|
EXPECT_NOT_NULL(file->data->data);
|
||||||
|
// cppcheck-suppress knownConditionTrueFalse
|
||||||
|
if (!file->data->data) {
|
||||||
|
file_destroy(file);
|
||||||
|
config_delete(config);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
memcpy(file->data->data, "new", 3);
|
||||||
|
file->data->size = 3;
|
||||||
|
|
||||||
|
EXPECT_EQ_INT(mkdir(root, 0755), 0);
|
||||||
|
EXPECT_EQ_INT(mkdir(directory, 0755), 0);
|
||||||
|
EXPECT_TRUE(file_write_to_disk(target, "old", 3, false, false));
|
||||||
|
EXPECT_EQ_INT(symlink("target", link), 0);
|
||||||
|
free(file->path);
|
||||||
|
file->path = str_dup("directory");
|
||||||
|
EXPECT_TRUE(file_save_to_disk(root, file, config));
|
||||||
|
free(file->path);
|
||||||
|
file->path = str_dup("link");
|
||||||
|
EXPECT_TRUE(file_save_to_disk(root, file, config));
|
||||||
|
|
||||||
|
free(file->path);
|
||||||
|
file->path = str_dup("backup.txt");
|
||||||
|
EXPECT_TRUE(file_write_to_disk(backup_file, "old", 3, false, false));
|
||||||
|
EXPECT_TRUE(file_save_to_disk(root, file, config));
|
||||||
|
EXPECT_TRUE(file_path_exists_secure(backup_file));
|
||||||
|
EXPECT_FALSE(file_path_exists_secure(backup));
|
||||||
|
|
||||||
|
file_destroy(file);
|
||||||
|
config_delete(config);
|
||||||
|
unlink(link);
|
||||||
|
unlink(target);
|
||||||
|
unlink(backup_file);
|
||||||
|
rmdir(directory);
|
||||||
|
rmdir(root);
|
||||||
|
}
|
||||||
|
|
||||||
static void test_file_write_to_disk_basic() {
|
static void test_file_write_to_disk_basic() {
|
||||||
const char* content = "Basic file_write_to_disk test";
|
const char* content = "Basic file_write_to_disk test";
|
||||||
EXPECT_TRUE(file_write_to_disk("test_file_write_to_disk_basic.txt", content, strlen(content),
|
EXPECT_TRUE(file_write_to_disk("test_file_write_to_disk_basic.txt", content, strlen(content),
|
||||||
@@ -491,6 +552,7 @@ void test_file() {
|
|||||||
test_file_load_data_missing_file();
|
test_file_load_data_missing_file();
|
||||||
test_file_save_to_disk();
|
test_file_save_to_disk();
|
||||||
test_file_save_to_disk_ignore_existing();
|
test_file_save_to_disk_ignore_existing();
|
||||||
|
test_file_save_to_disk_ignore_existing_entry_types();
|
||||||
test_file_write_to_disk_basic();
|
test_file_write_to_disk_basic();
|
||||||
test_file_write_to_disk_creates_dirs();
|
test_file_write_to_disk_creates_dirs();
|
||||||
test_file_write_to_disk_does_not_follow_symlink();
|
test_file_write_to_disk_does_not_follow_symlink();
|
||||||
|
|||||||
Reference in New Issue
Block a user