identity mapping: --numeric-ids / --usermap / --groupmap / --chown
CI / lint (pull_request) Failing after 3s
CI / build-and-test (pull_request) Skipped
CI / sanitizers (address) (pull_request) Skipped
CI / sanitizers (undefined) (pull_request) Skipped
CI / fuzz-build (pull_request) Skipped
CI / coverage (pull_request) Skipped
CI / valgrind (pull_request) Skipped

Receiver-side ownership application, opt-in and privilege-gated:
- OFF for every existing transfer (plain -M/--preserve still never applies
  ownership); only triggers on an explicit identity flag + receiver permission.
- EPERM/EACCES warn-and-continue (never aborts); other fchown errors escalate.
- fd-relative fchown after the file is written (symlink-safe, confined).
- New src/shared/identity.{c,h}; config fields numeric_ids / chown uid/gid /
  usermap + groupmap id-pair tables cross the wire; PROTOCOL_VERSION 2.10.0
  -> 2.11.0. CLI in client_cli.c; per-connection snapshot in server.c.
- Review fixes: EPERM/EACCES-only warn-and-continue, prominent root-receiver
  notice, identity_clear_active on early server error paths, --numeric-ids
  kept inert standalone (removed from activation trigger set).
This commit is contained in:
2026-09-08 18:23:43 +02:00
parent 829e760086
commit 53ce00b830
12 changed files with 1094 additions and 8 deletions
+38
View File
@@ -6,6 +6,7 @@
#include "delta.h"
#include "file_list.h"
#include "filter.h"
#include "identity.h"
#include "log.h"
#include "protocol.h"
#include "transport_tcp.h"
@@ -530,6 +531,7 @@ static const OptionEntry OPTION_TABLE[] = {
{"--from0", "-0", OPT_FLAG, offsetof(Config, from0)},
{"--cvs-exclude", "-C", OPT_FLAG, offsetof(Config, cvs_exclude)},
{"-F", NULL, OPT_FLAG, offsetof(Config, per_dir_filter)},
{"--numeric-ids", NULL, OPT_FLAG, offsetof(Config, numeric_ids)},
};
/* Only boolean options with no required argument are safe to negate. */
@@ -1108,6 +1110,42 @@ int parse_args(Config* config, int argc, char* argv[], int* positional_args,
}
if (set_basis_dest_option(config, BASIS_DEST_LINK, argv[++i], "--link-dest") != 0)
return -1;
} else if (strncmp(argv[i], "--usermap=", 10) == 0) {
if (identity_parse_map(config, argv[i] + 10, false) != 0)
return -1;
config->use_metadata = true;
} else if (opt_is(argv[i], "--usermap", NULL)) {
if (i + 1 >= argc) {
log_message(LOG_LEVEL_ERROR, "missing argument for %s", argv[i]);
return -1;
}
if (identity_parse_map(config, argv[++i], false) != 0)
return -1;
config->use_metadata = true;
} else if (strncmp(argv[i], "--groupmap=", 11) == 0) {
if (identity_parse_map(config, argv[i] + 11, true) != 0)
return -1;
config->use_metadata = true;
} else if (opt_is(argv[i], "--groupmap", NULL)) {
if (i + 1 >= argc) {
log_message(LOG_LEVEL_ERROR, "missing argument for %s", argv[i]);
return -1;
}
if (identity_parse_map(config, argv[++i], true) != 0)
return -1;
config->use_metadata = true;
} else if (strncmp(argv[i], "--chown=", 8) == 0) {
if (identity_parse_chown(config, argv[i] + 8) != 0)
return -1;
config->use_metadata = true;
} else if (opt_is(argv[i], "--chown", NULL)) {
if (i + 1 >= argc) {
log_message(LOG_LEVEL_ERROR, "missing argument for %s", argv[i]);
return -1;
}
if (identity_parse_chown(config, argv[++i]) != 0)
return -1;
config->use_metadata = true;
} else if (argv[i][0] == '-') {
char* escaped = output_escape(argv[i], false);
fprintf(stderr, "Unknown option: %s\n", escaped ? escaped : "<allocation failed>");
+13
View File
@@ -129,6 +129,19 @@ void print_usage(void) {
printf(" -M, --preserve Preserve file metadata\n");
printf(" -E, --executability Preserve executable permission bits\n");
printf(" --chmod <changes> Modify transferred permissions (rsync syntax)\n");
printf(" --numeric-ids Do not map uid/gid by name: use the source numeric\n");
printf(" ids directly when applying ownership\n");
printf(" --usermap=MAP Map usernames when applying ownership: comma-separated\n");
printf(" FROM:TO rules, first match wins. FROM/TO are names\n");
printf(" (resolved on the source machine), * (match any /\n");
printf(" current user), or @N numeric ids. e.g. *:nobody\n");
printf(" --groupmap=MAP Map group names when applying ownership (same syntax)\n");
printf(" --chown=USER:GROUP Override the ownership of transferred files. Forms:\n");
printf(" USER:GROUP, USER (owner only), :GROUP (group only); a\n");
printf(" value of * means the current/root user as appropriate.\n");
printf(" Names resolve on the source machine; @N for numerics.\n");
printf(" Note: -M is already FastSync's preserve flag; these use\n");
printf(" long forms only.\n");
printf(" --chunk-size <n> Chunk size in bytes (default: %d)\n", DEFAULT_CHUNK_SIZE);
printf(" --source-dir <path> Source directory\n");
printf(" --dest-dir <path> Destination directory\n");