feat: implement -R/--relative, --no-implied-dirs, --dirs/-d, --mkpath
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
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).
This commit is contained in:
+41
-22
@@ -547,34 +547,54 @@ static void test_parse_args_unknown_option() {
|
||||
config_delete(cfg);
|
||||
}
|
||||
|
||||
/* Directory aliases must report the unsupported directory-only behavior clearly. */
|
||||
static void test_parse_args_rejects_dirs_aliases() {
|
||||
static const char* const options[] = {"--dirs", "--old-dirs", "--old-d"};
|
||||
|
||||
/* -d/--dirs and the rsync --old-dirs/--old-d aliases all enable directory-only
|
||||
* transfers (--dirs maps every spelling onto the same config field). */
|
||||
static void test_parse_args_dirs_aliases() {
|
||||
static const char* const options[] = {"--dirs", "-d", "--old-dirs", "--old-d"};
|
||||
for (size_t i = 0; i < sizeof(options) / sizeof(options[0]); i++) {
|
||||
Config* cfg = config_create();
|
||||
char* argv[] = {"fastsync", (char*)options[i], "/src", "/dst"};
|
||||
int positional_args[2];
|
||||
int positional_count = 0;
|
||||
FILE* log_file = tmpfile();
|
||||
char log_buffer[256] = {0};
|
||||
|
||||
EXPECT_NOT_NULL(log_file);
|
||||
log_set_file(log_file);
|
||||
|
||||
EXPECT_EQ_INT(parse_args(cfg, 4, argv, positional_args, &positional_count), -1);
|
||||
fflush(log_file);
|
||||
rewind(log_file);
|
||||
EXPECT_TRUE(fread(log_buffer, 1, sizeof(log_buffer) - 1, log_file) > 0);
|
||||
EXPECT_TRUE(strstr(log_buffer, options[i]) != NULL);
|
||||
EXPECT_TRUE(strstr(log_buffer, "directory-only transfer is not implemented") != NULL);
|
||||
EXPECT_TRUE(strstr(log_buffer, "requires --dirs") == NULL);
|
||||
log_set_file(NULL);
|
||||
fclose(log_file);
|
||||
EXPECT_EQ_INT(parse_args(cfg, 4, argv, positional_args, &positional_count), 0);
|
||||
EXPECT_TRUE(cfg->dirs);
|
||||
config_delete(cfg);
|
||||
}
|
||||
}
|
||||
|
||||
/* -R/--relative, --no-implied-dirs and --mkpath are plain boolean flags. */
|
||||
static void test_parse_args_relative_no_implied_mkpath() {
|
||||
Config* cfg = config_create();
|
||||
char* argv[] = {"fastsync", "-R", "/src", "/dst"};
|
||||
int positional_args[2];
|
||||
int positional_count = 0;
|
||||
EXPECT_EQ_INT(parse_args(cfg, 4, argv, positional_args, &positional_count), 0);
|
||||
EXPECT_TRUE(cfg->relative);
|
||||
config_delete(cfg);
|
||||
|
||||
cfg = config_create();
|
||||
positional_count = 0;
|
||||
char* long_argv[] = {"fastsync", "--relative", "/src", "/dst"};
|
||||
EXPECT_EQ_INT(parse_args(cfg, 4, long_argv, positional_args, &positional_count), 0);
|
||||
EXPECT_TRUE(cfg->relative);
|
||||
config_delete(cfg);
|
||||
|
||||
cfg = config_create();
|
||||
positional_count = 0;
|
||||
char* noimplied_argv[] = {"fastsync", "--no-implied-dirs", "/src", "/dst"};
|
||||
EXPECT_EQ_INT(parse_args(cfg, 4, noimplied_argv, positional_args, &positional_count), 0);
|
||||
EXPECT_TRUE(cfg->no_implied_dirs);
|
||||
EXPECT_FALSE(cfg->relative);
|
||||
config_delete(cfg);
|
||||
|
||||
cfg = config_create();
|
||||
positional_count = 0;
|
||||
char* mkpath_argv[] = {"fastsync", "--mkpath", "/src", "/dst"};
|
||||
EXPECT_EQ_INT(parse_args(cfg, 4, mkpath_argv, positional_args, &positional_count), 0);
|
||||
EXPECT_TRUE(cfg->mkpath);
|
||||
config_delete(cfg);
|
||||
}
|
||||
|
||||
/* --del is recognized as the rsync alias, but its timing mode is not implemented. */
|
||||
static void test_parse_args_delete_during_alias_unimplemented() {
|
||||
static const char* const options[] = {"--del", "--delete-during"};
|
||||
@@ -609,8 +629,6 @@ static void test_parse_args_rejects_unimplemented_options() {
|
||||
"--delete-after",
|
||||
"--max-delete",
|
||||
"--prune-empty-dirs",
|
||||
"-R",
|
||||
"--relative",
|
||||
"-e",
|
||||
"--rsh",
|
||||
"--rsync-path",
|
||||
@@ -1522,7 +1540,8 @@ void test_client_cli() {
|
||||
test_parse_args_max_alloc_sizes();
|
||||
test_parse_args_rejects_invalid_max_alloc();
|
||||
test_parse_args_unknown_option();
|
||||
test_parse_args_rejects_dirs_aliases();
|
||||
test_parse_args_dirs_aliases();
|
||||
test_parse_args_relative_no_implied_mkpath();
|
||||
test_parse_args_delete_during_alias_unimplemented();
|
||||
test_parse_args_rejects_unimplemented_options();
|
||||
test_parse_args_quiet();
|
||||
|
||||
Reference in New Issue
Block a user