- #286: --numeric-ids is a mapping modifier only; it no longer activates chown by itself (identity_active_enabled/owner/group predicates), and --fake-super stores the resolved mapping instead of real-chowning. - #286: apply owner/group to directories via the deferred directory metadata path; capture+transmit+apply directory xattrs/ACLs (-aX/-aA), including default ACLs, in STATUS_MKDIR/STATUS_DIR_TIMES. - #294: --usermap/--groupmap support inclusive ranges, '*', empty FROM (unnamed ids), and receiver-side TO name resolution; --chown mixing with a same-side map is rejected like rsync. - Protocol 2.22.0 -> 2.23.0 (map wire entry gains from_hi + to_name; dir frames gain a bounded xattr block).
This commit is contained in:
@@ -1830,6 +1830,75 @@ static bool cli_handle_checksum_options(CliParseCtx* ctx) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Determine whether a --chown spec sets the owner and/or group side, honoring
|
||||
* the same escape-aware splitting as identity_parse_chown(): a `\:` is a literal
|
||||
* colon, not a field separator. */
|
||||
static void chown_spec_sides(const char* value, bool* has_owner, bool* has_group) {
|
||||
*has_owner = false;
|
||||
*has_group = false;
|
||||
if (!value)
|
||||
return;
|
||||
bool split = false;
|
||||
for (const char* p = value; *p; p++) {
|
||||
if (*p == '\\' && p[1] == ':') {
|
||||
p++;
|
||||
continue;
|
||||
}
|
||||
if (*p == ':') {
|
||||
split = true;
|
||||
continue;
|
||||
}
|
||||
if (split)
|
||||
*has_group = true;
|
||||
else
|
||||
*has_owner = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* rsync refuses to mix --chown with --usermap/--groupmap on the SAME side
|
||||
* ("--usermap conflicts with prior --chown"). `chown_value` is non-NULL only
|
||||
* for the --chown option itself. Returns true and records a parse error when
|
||||
* the new option conflicts with one already seen. */
|
||||
static bool mapping_option_conflicts(CliParseCtx* ctx, const char* optname, bool is_group,
|
||||
const char* chown_value) {
|
||||
const Config* config = ctx->config;
|
||||
if (chown_value) {
|
||||
bool has_owner;
|
||||
bool has_group;
|
||||
chown_spec_sides(chown_value, &has_owner, &has_group);
|
||||
if (has_owner && config->usermap_count > 0) {
|
||||
log_message(LOG_LEVEL_ERROR, "%s conflicts with prior --usermap", optname);
|
||||
ctx->exit_code = -1;
|
||||
return true;
|
||||
}
|
||||
if (has_group && config->groupmap_count > 0) {
|
||||
log_message(LOG_LEVEL_ERROR, "%s conflicts with prior --groupmap", optname);
|
||||
ctx->exit_code = -1;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (!is_group && config->chown_uid_set) {
|
||||
log_message(LOG_LEVEL_ERROR, "%s conflicts with prior --chown", optname);
|
||||
ctx->exit_code = -1;
|
||||
return true;
|
||||
}
|
||||
if (is_group && config->chown_gid_set) {
|
||||
log_message(LOG_LEVEL_ERROR, "%s conflicts with prior --chown", optname);
|
||||
ctx->exit_code = -1;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool usermap_conflicts_with_chown(CliParseCtx* ctx, const char* optname, bool is_group) {
|
||||
return mapping_option_conflicts(ctx, optname, is_group, NULL);
|
||||
}
|
||||
|
||||
static bool chown_conflicts_with_map(CliParseCtx* ctx, const char* optname, const char* value) {
|
||||
return mapping_option_conflicts(ctx, optname, false, value);
|
||||
}
|
||||
|
||||
/* Remote-option, basis-directory and identity-mapping options. Returns true
|
||||
* when the argument was consumed. */
|
||||
static bool cli_handle_remote_basis_options(CliParseCtx* ctx) {
|
||||
@@ -1902,6 +1971,8 @@ static bool cli_handle_remote_basis_options(CliParseCtx* ctx) {
|
||||
return true;
|
||||
}
|
||||
if (strncmp(arg, "--usermap=", 10) == 0) {
|
||||
if (usermap_conflicts_with_chown(ctx, "--usermap", false))
|
||||
return true;
|
||||
if (identity_parse_map(config, arg + 10, false) != 0) {
|
||||
ctx->exit_code = -1;
|
||||
return true;
|
||||
@@ -1915,6 +1986,8 @@ static bool cli_handle_remote_basis_options(CliParseCtx* ctx) {
|
||||
ctx->exit_code = -1;
|
||||
return true;
|
||||
}
|
||||
if (usermap_conflicts_with_chown(ctx, "--usermap", false))
|
||||
return true;
|
||||
if (identity_parse_map(config, ctx->argv[++ctx->i], false) != 0) {
|
||||
ctx->exit_code = -1;
|
||||
return true;
|
||||
@@ -1923,6 +1996,8 @@ static bool cli_handle_remote_basis_options(CliParseCtx* ctx) {
|
||||
return true;
|
||||
}
|
||||
if (strncmp(arg, "--groupmap=", 11) == 0) {
|
||||
if (usermap_conflicts_with_chown(ctx, "--groupmap", true))
|
||||
return true;
|
||||
if (identity_parse_map(config, arg + 11, true) != 0) {
|
||||
ctx->exit_code = -1;
|
||||
return true;
|
||||
@@ -1936,6 +2011,8 @@ static bool cli_handle_remote_basis_options(CliParseCtx* ctx) {
|
||||
ctx->exit_code = -1;
|
||||
return true;
|
||||
}
|
||||
if (usermap_conflicts_with_chown(ctx, "--groupmap", true))
|
||||
return true;
|
||||
if (identity_parse_map(config, ctx->argv[++ctx->i], true) != 0) {
|
||||
ctx->exit_code = -1;
|
||||
return true;
|
||||
@@ -1944,6 +2021,8 @@ static bool cli_handle_remote_basis_options(CliParseCtx* ctx) {
|
||||
return true;
|
||||
}
|
||||
if (strncmp(arg, "--chown=", 8) == 0) {
|
||||
if (chown_conflicts_with_map(ctx, "--chown", arg + 8))
|
||||
return true;
|
||||
if (identity_parse_chown(config, arg + 8) != 0) {
|
||||
ctx->exit_code = -1;
|
||||
return true;
|
||||
@@ -1960,6 +2039,8 @@ static bool cli_handle_remote_basis_options(CliParseCtx* ctx) {
|
||||
ctx->exit_code = -1;
|
||||
return true;
|
||||
}
|
||||
if (chown_conflicts_with_map(ctx, "--chown", ctx->argv[ctx->i + 1]))
|
||||
return true;
|
||||
if (identity_parse_chown(config, ctx->argv[++ctx->i]) != 0) {
|
||||
ctx->exit_code = -1;
|
||||
return true;
|
||||
|
||||
Reference in New Issue
Block a user