From ce64fcd4b17878bdc0925e39bbf4ce62ec5979cd Mon Sep 17 00:00:00 2001 From: TapTap Date: Thu, 3 Sep 2026 18:31:09 +0200 Subject: [PATCH 1/2] feat: recognize rsync old-dirs aliases --- README.md | 3 +++ RSYNC_COMPAT.md | 2 +- src/client/client_cli.c | 8 ++++++++ src/client/usage.c | 2 ++ tests/test_client_cli.c | 16 ++++++++++++++++ 5 files changed, 30 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index fdbd9f3..be6fbc7 100644 --- a/README.md +++ b/README.md @@ -68,6 +68,9 @@ replacement for every rsync feature or protocol mode. - Sparse-file handling does not yet preserve all holes correctly. - `--partial`, `--partial-dir`, `--append`, and `--append-verify` are not yet full rsync-style resumable transfers. +- `--dirs` is not implemented. Its compatibility aliases `--old-dirs` and + `--old-d` are recognized but rejected explicitly rather than silently using + FastSync's recursive directory behavior. - Several rsync short options currently have FastSync-specific meanings. Do not assume every short option is interchangeable yet. diff --git a/RSYNC_COMPAT.md b/RSYNC_COMPAT.md index 4b236ec..8484928 100644 --- a/RSYNC_COMPAT.md +++ b/RSYNC_COMPAT.md @@ -71,7 +71,7 @@ This document maps rsync's full feature set to FastSync's current implementation | `-r`, `--recursive` | Recurse into directories | ✅ Implemented | Default behavior | | `-R`, `--relative` | Use relative path names | ❌ Not Implemented | Removed because it had no effect | | `--no-implied-dirs` | Don't send implied dirs with -R | ❌ Not Implemented | | -| `-d`, `--dirs` | Transfer dirs without recursing | ❌ Not Implemented | | +| `-d`, `--dirs`, `--old-dirs`, `--old-d` | Transfer dirs without recursing | ❌ Not Implemented | The aliases are recognized and rejected explicitly; they depend on the unimplemented `--dirs` behavior | | `--mkpath` | Create missing path components | ❌ Not Implemented | | ## 5. Transfer Modifications diff --git a/src/client/client_cli.c b/src/client/client_cli.c index 191cc07..0de3a4f 100644 --- a/src/client/client_cli.c +++ b/src/client/client_cli.c @@ -125,6 +125,7 @@ typedef enum { OPT_POS_INT, OPT_NONNEG_INT, OPT_ULL, + OPT_UNSUPPORTED, } OptKind; typedef struct { @@ -153,6 +154,8 @@ static const OptionEntry OPTION_TABLE[] = { {"--sparse", "-S", OPT_FLAG, offsetof(Config, preserve_sparse)}, {"--inplace", NULL, OPT_FLAG, offsetof(Config, inplace)}, {"--checksum", NULL, OPT_FLAG, offsetof(Config, checksum)}, + {"--dirs", "--old-dirs", OPT_UNSUPPORTED, 0}, + {"--old-d", NULL, OPT_UNSUPPORTED, 0}, {"--source-dir", NULL, OPT_STRING, offsetof(Config, send_directory)}, {"--dest-dir", NULL, OPT_STRING, offsetof(Config, receive_root_directory)}, @@ -203,6 +206,11 @@ static int apply_table_option(Config* config, const OptionEntry* entry, const ch *(unsigned long long*)field = v; return 0; } + case OPT_UNSUPPORTED: + log_message(LOG_LEVEL_ERROR, + "%s requires --dirs, which is not implemented; refusing to ignore option", + entry->name); + return -1; } return -1; } diff --git a/src/client/usage.c b/src/client/usage.c index 2f800af..4689df9 100644 --- a/src/client/usage.c +++ b/src/client/usage.c @@ -21,6 +21,8 @@ void print_usage(void) { printf(" -p SSH port (default: 22)\n"); printf(" --progress Show transfer progress\n"); printf(" --delete Delete files on receiver not in source\n"); + printf( + " --dirs, --old-dirs, --old-d Transfer directories without recursing (not implemented)\n"); printf(" --exclude Exclude files matching pattern\n"); printf(" --include Only include files matching pattern\n"); printf(" --exclude-from Read exclude patterns from file\n"); diff --git a/tests/test_client_cli.c b/tests/test_client_cli.c index 656fd40..d998a3e 100644 --- a/tests/test_client_cli.c +++ b/tests/test_client_cli.c @@ -260,6 +260,21 @@ static void test_parse_args_unknown_option() { config_delete(cfg); } +/* Directory aliases require the not-yet-implemented --dirs behavior. */ +static void test_parse_args_rejects_dirs_aliases() { + static const char* const options[] = {"--dirs", "--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; + + EXPECT_EQ_INT(parse_args(cfg, 4, argv, positional_args, &positional_count), -1); + config_delete(cfg); + } +} + /* Parsed-but-unimplemented options must fail instead of being silently accepted. */ static void test_parse_args_rejects_unimplemented_options() { static const char* const options[] = {"-q", @@ -358,6 +373,7 @@ void test_client_cli() { test_parse_args_invalid_compression_level(); test_parse_args_valid_compression_level(); test_parse_args_unknown_option(); + test_parse_args_rejects_dirs_aliases(); test_parse_args_rejects_unimplemented_options(); test_parse_args_archive(); } From 9f23b23893acf4831eac958db868985c0f5d54c2 Mon Sep 17 00:00:00 2001 From: TapTap Date: Thu, 3 Sep 2026 22:32:09 +0200 Subject: [PATCH 2/2] fix: clarify unsupported directory option diagnostics --- src/client/client_cli.c | 18 +++++++++++------- tests/test_client_cli.c | 17 ++++++++++++++++- 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/src/client/client_cli.c b/src/client/client_cli.c index 0de3a4f..8179c52 100644 --- a/src/client/client_cli.c +++ b/src/client/client_cli.c @@ -187,7 +187,8 @@ static const OptionEntry* find_table_option(const char* arg) { return NULL; } -static int apply_table_option(Config* config, const OptionEntry* entry, const char* value) { +static int apply_table_option(Config* config, const OptionEntry* entry, const char* option_name, + const char* value) { void* field = (char*)config + entry->offset; switch (entry->kind) { case OPT_FLAG: @@ -208,8 +209,8 @@ static int apply_table_option(Config* config, const OptionEntry* entry, const ch } case OPT_UNSUPPORTED: log_message(LOG_LEVEL_ERROR, - "%s requires --dirs, which is not implemented; refusing to ignore option", - entry->name); + "%s: directory-only transfer is not implemented; refusing to ignore option", + option_name); return -1; } return -1; @@ -221,14 +222,17 @@ int parse_args(Config* config, int argc, char* argv[], int* positional_args, for (int i = 1; i < argc; i++) { const OptionEntry* entry = find_table_option(argv[i]); if (entry) { + const char* option_name = argv[i]; + const char* value = NULL; if (entry->kind != OPT_FLAG) { - if (i + 1 >= argc) { + if (entry->kind != OPT_UNSUPPORTED && i + 1 >= argc) { log_message(LOG_LEVEL_ERROR, "missing argument for %s", entry->name); return -1; } - if (apply_table_option(config, entry, argv[++i]) != 0) - return -1; - } else if (apply_table_option(config, entry, NULL) != 0) { + if (entry->kind != OPT_UNSUPPORTED) + value = argv[++i]; + } + if (apply_table_option(config, entry, option_name, value) != 0) { return -1; } continue; diff --git a/tests/test_client_cli.c b/tests/test_client_cli.c index d998a3e..de1bc5a 100644 --- a/tests/test_client_cli.c +++ b/tests/test_client_cli.c @@ -1,8 +1,10 @@ #include "test_client_cli.h" #include "client_validation.h" #include "config.h" +#include "log.h" #include "test_utils.h" #include "utils.h" +#include #include #include #include @@ -260,7 +262,7 @@ static void test_parse_args_unknown_option() { config_delete(cfg); } -/* Directory aliases require the not-yet-implemented --dirs behavior. */ +/* 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"}; @@ -269,8 +271,21 @@ static void test_parse_args_rejects_dirs_aliases() { 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); config_delete(cfg); } }