From 5350290f528583bed6c3a7bd9de12a61d4e39f1e Mon Sep 17 00:00:00 2001 From: TapTap Date: Thu, 3 Sep 2026 21:21:22 +0200 Subject: [PATCH] fix: reject unsupported rsync client stderr mode --- RSYNC_COMPAT.md | 12 ++++++------ src/client/client_cli.c | 10 ++++++---- src/client/usage.c | 2 +- src/shared/log.h | 2 +- tests/test_client_cli.c | 27 ++++++++++++++++----------- 5 files changed, 30 insertions(+), 23 deletions(-) diff --git a/RSYNC_COMPAT.md b/RSYNC_COMPAT.md index cc75281..f12f2f6 100644 --- a/RSYNC_COMPAT.md +++ b/RSYNC_COMPAT.md @@ -6,11 +6,11 @@ This document maps rsync's full feature set to FastSync's current implementation | Status | Count | Description | |--------|-------|-------------| -| ✅ Implemented | 34 | Feature works end-to-end | -| 🔀 Alt Arg | 3 | Functionality exists but under different flag/semantics | -| ⚠️ Partial | 1 | Flag parsed/stored but behavior incomplete | -| ❌ Not Implemented | 98 | Flag not recognized or no behavior | -| **Total** | **136** | | +| ✅ Implemented | 35 | Feature works end-to-end | +| 🔀 Alt Arg | 4 | Functionality exists but under different flag/semantics | +| ⚠️ Partial | 5 | Flag parsed/stored but behavior incomplete | +| ❌ Not Implemented | 97 | Flag not recognized or no behavior | +| **Total** | **141** | | --- @@ -25,7 +25,7 @@ This document maps rsync's full feature set to FastSync's current implementation | `-V`, `--version` | Print version | ✅ Implemented | | | `--info=FLAGS` | Fine-grained info verbosity | ❌ Not Implemented | Removed because it had no effect | | `--debug=FLAGS` | Fine-grained debug verbosity | ❌ Not Implemented | Removed because it had no effect | -| `--stderr=MODE` | Change stderr output mode | ⚠️ Partial | `errors` (default), `all`, and `client` are accepted; `client` preserves local client routing because FastSync has no rsync message channel | +| `--stderr=MODE` | Change stderr output mode | ⚠️ Partial | `errors` (default) and `all` are supported; `client` is rejected because FastSync has no rsync message channel | | `--no-motd` | Suppress daemon MOTD | ❌ Not Implemented | | | `--exclude=PATTERN` | Exclude files matching pattern | ✅ Implemented | Glob matching in scanner | | `--include=PATTERN` | Include files matching pattern | ✅ Implemented | Glob matching in scanner | diff --git a/src/client/client_cli.c b/src/client/client_cli.c index e85b545..dce9c79 100644 --- a/src/client/client_cli.c +++ b/src/client/client_cli.c @@ -91,10 +91,12 @@ static int set_stderr_mode(const char* value) { log_set_stderr_mode(LOG_STDERR_ERRORS); else if (strcmp(value, "all") == 0 || strcmp(value, "a") == 0) log_set_stderr_mode(LOG_STDERR_ALL); - else if (strcmp(value, "client") == 0 || strcmp(value, "c") == 0) - log_set_stderr_mode(LOG_STDERR_CLIENT); - else { - log_message(LOG_LEVEL_ERROR, "--stderr must be errors, all, or client"); + else if (strcmp(value, "client") == 0 || strcmp(value, "c") == 0) { + log_message(LOG_LEVEL_ERROR, + "--stderr=client is not supported: FastSync has no client message channel"); + return -1; + } else { + log_message(LOG_LEVEL_ERROR, "--stderr must be errors or all"); return -1; } return 0; diff --git a/src/client/usage.c b/src/client/usage.c index 69fd5cf..5c9c5f1 100644 --- a/src/client/usage.c +++ b/src/client/usage.c @@ -58,7 +58,7 @@ void print_usage(void) { printf(" --stats Print transfer statistics at end\n"); printf(" --max-depth Maximum directory depth (0=unlimited)\n"); printf(" --log-file Write log messages to file\n"); - printf(" --stderr=MODE Route logging to stderr: errors, all, or client\n"); + printf(" --stderr=MODE Route logging to stderr: errors or all\n"); printf(" --partial Keep partial files on interrupted transfer\n"); printf(" --partial-dir Directory for partial files\n"); printf(" --fastsync-server-path \n"); diff --git a/src/shared/log.h b/src/shared/log.h index 2fe216d..24237c8 100644 --- a/src/shared/log.h +++ b/src/shared/log.h @@ -4,7 +4,7 @@ #include typedef enum { LOG_LEVEL_DEBUG, LOG_LEVEL_INFO, LOG_LEVEL_WARNING, LOG_LEVEL_ERROR } LogLevel; -typedef enum { LOG_STDERR_ERRORS, LOG_STDERR_ALL, LOG_STDERR_CLIENT } LogStderrMode; +typedef enum { LOG_STDERR_ERRORS, LOG_STDERR_ALL } LogStderrMode; void log_message(LogLevel log_level, const char* message, ...); void log_perror(const char* context); diff --git a/tests/test_client_cli.c b/tests/test_client_cli.c index e809112..6e53f2f 100644 --- a/tests/test_client_cli.c +++ b/tests/test_client_cli.c @@ -341,9 +341,9 @@ static void test_parse_args_archive() { } static void test_parse_args_stderr_modes() { - static const char* const modes[] = {"errors", "all", "client", "e", "a", "c"}; - static const LogStderrMode expected[] = {LOG_STDERR_ERRORS, LOG_STDERR_ALL, LOG_STDERR_CLIENT, - LOG_STDERR_ERRORS, LOG_STDERR_ALL, LOG_STDERR_CLIENT}; + static const char* const modes[] = {"errors", "all", "e", "a"}; + static const LogStderrMode expected[] = {LOG_STDERR_ERRORS, LOG_STDERR_ALL, LOG_STDERR_ERRORS, + LOG_STDERR_ALL}; for (size_t i = 0; i < sizeof(modes) / sizeof(modes[0]); i++) { Config* cfg = config_create(); char option[32]; @@ -358,13 +358,18 @@ static void test_parse_args_stderr_modes() { log_set_stderr_mode(LOG_STDERR_ERRORS); } -static void test_parse_args_rejects_invalid_stderr_mode() { - Config* cfg = config_create(); - char* argv[] = {"fastsync", "--stderr=invalid", "/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); +static void test_parse_args_rejects_unsupported_stderr_modes() { + static const char* const modes[] = {"client", "c", "invalid"}; + for (size_t i = 0; i < sizeof(modes) / sizeof(modes[0]); i++) { + Config* cfg = config_create(); + char option[32]; + snprintf(option, sizeof(option), "--stderr=%s", modes[i]); + char* argv[] = {"fastsync", option, "/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); + } log_set_stderr_mode(LOG_STDERR_ERRORS); } @@ -390,5 +395,5 @@ void test_client_cli() { test_parse_args_rejects_unimplemented_options(); test_parse_args_archive(); test_parse_args_stderr_modes(); - test_parse_args_rejects_invalid_stderr_mode(); + test_parse_args_rejects_unsupported_stderr_modes(); }