Merge branch 'feat/parity-ownership' into feat/rsync-parity

# Conflicts:
#	src/shared/config.h
#	tests/test_config.c
This commit is contained in:
2026-09-15 23:24:58 +02:00
24 changed files with 1117 additions and 378 deletions
+36 -12
View File
@@ -1345,13 +1345,19 @@ static void test_config_identity_wire_roundtrip() {
send_cfg->usermap_count = 2;
send_cfg->usermap = calloc(2, sizeof(IdentityMap));
send_cfg->usermap[0].from = IDENTITY_MATCH_ANY;
send_cfg->usermap[0].from_hi = IDENTITY_MATCH_ANY;
send_cfg->usermap[0].to = 65534;
send_cfg->usermap[0].to_name = NULL;
send_cfg->usermap[1].from = 1000;
send_cfg->usermap[1].from_hi = 1000;
send_cfg->usermap[1].to = 1000;
send_cfg->usermap[1].to_name = NULL;
send_cfg->groupmap_count = 1;
send_cfg->groupmap = calloc(1, sizeof(IdentityMap));
send_cfg->groupmap[0].from = 0;
send_cfg->groupmap[0].from_hi = 0;
send_cfg->groupmap[0].to = IDENTITY_CURRENT;
send_cfg->groupmap[0].to_name = str_dup("root");
int p[2];
EXPECT_EQ_INT(socketpair(AF_UNIX, SOCK_STREAM, 0, p), 0);
@@ -1367,9 +1373,12 @@ static void test_config_identity_wire_roundtrip() {
ok = recv->numeric_ids && recv->chown_uid_set && recv->chown_uid == 1001 &&
recv->chown_gid_set && recv->chown_gid == IDENTITY_CURRENT && recv->usermap_count == 2 &&
recv->groupmap_count == 1 && recv->usermap[0].from == IDENTITY_MATCH_ANY &&
recv->usermap[0].to == 65534 && recv->usermap[1].from == 1000 &&
recv->usermap[1].to == 1000 && recv->groupmap[0].from == 0 &&
recv->groupmap[0].to == IDENTITY_CURRENT;
recv->usermap[0].from_hi == IDENTITY_MATCH_ANY && recv->usermap[0].to == 65534 &&
recv->usermap[0].to_name == NULL && recv->usermap[1].from == 1000 &&
recv->usermap[1].from_hi == 1000 && recv->usermap[1].to == 1000 &&
recv->groupmap[0].from == 0 && recv->groupmap[0].from_hi == 0 &&
recv->groupmap[0].to == IDENTITY_CURRENT && recv->groupmap[0].to_name != NULL &&
strcmp(recv->groupmap[0].to_name, "root") == 0;
}
config_delete(recv);
close(p[0]);
@@ -1399,7 +1408,8 @@ static void test_config_receive_rejects_invalid_identity() {
c->receive_root_directory = str_dup("/dst");
c->usermap_count = 1;
c->usermap = calloc(1, sizeof(IdentityMap));
c->usermap[0].from = -2; /* below IDENTITY_MATCH_ANY */
c->usermap[0].from = -3; /* below IDENTITY_MATCH_UNNAMED */
c->usermap[0].from_hi = -3;
c->usermap[0].to = 0;
EXPECT_FALSE(roundtrip_config_ok(c));
config_delete(c);
@@ -2101,8 +2111,10 @@ static void test_identity_explicit_ownership_requested() {
}
/* P7 Wave E hardening (A3): --super no longer implies raw numeric-id
preservation, so it must never enable ownership application on its own; an
explicit identity flag is required. */
preservation, so it must never enable ownership application on its own.
#286: --numeric-ids is a mapping MODIFIER only and is likewise inert on its
own; a real ownership request (-o/-g or an explicit identity flag) is
required to activate chown. */
static void test_super_does_not_imply_numeric() {
Config* c = config_create();
EXPECT_NOT_NULL(c);
@@ -2112,6 +2124,9 @@ static void test_super_does_not_imply_numeric() {
EXPECT_FALSE(identity_active_enabled());
c->numeric_ids = true;
EXPECT_TRUE(identity_set_active(c));
EXPECT_FALSE(identity_active_enabled()); /* mapping modifier only */
c->preserve_owner = true;
EXPECT_TRUE(identity_set_active(c));
EXPECT_TRUE(identity_active_enabled());
identity_clear_active();
config_delete(c);
@@ -2635,13 +2650,19 @@ static void golden_config_populate(Config* c) {
c->usermap_count = 2;
c->usermap = calloc(2, sizeof(IdentityMap));
c->usermap[0].from = IDENTITY_MATCH_ANY;
c->usermap[0].from_hi = IDENTITY_MATCH_ANY;
c->usermap[0].to = 1000;
c->usermap[0].to_name = NULL;
c->usermap[1].from = 5;
c->usermap[1].from_hi = 9;
c->usermap[1].to = 6;
c->usermap[1].to_name = NULL;
c->groupmap_count = 1;
c->groupmap = calloc(1, sizeof(IdentityMap));
c->groupmap[0].from = 7;
c->groupmap[0].from_hi = 7;
c->groupmap[0].to = 8;
c->groupmap[0].to_name = str_dup("root");
c->preserve_atimes = true;
c->preserve_crtimes = false;
c->omit_dir_times = true;
@@ -2668,11 +2689,11 @@ static void golden_config_populate(Config* c) {
/* The pinned golden frame (protocol 2.23.0). The values below are the only
* thing that ties the generated table to the historical wire format; update
* them ONLY with a PROTOCOL_VERSION bump and a documented reason. The 2.23.0
* delete-semantics wave keeps the config-frame LAYOUT unchanged, but the
* embedded version string moves to "2.23.0", so the byte-exact hash changes
* while the length stays 653. */
#define GOLDEN_WIRE_LEN 653
#define GOLDEN_WIRE_HASH 3267254725292157519ULL
* rsync-parity wave changes the config-frame layout (map-entry range + TO name,
* plus other wire changes landing in this version); the byte-exact hash is
* recomputed for the merged layout. */
#define GOLDEN_WIRE_LEN 693
#define GOLDEN_WIRE_HASH 6341115972171444885ULL
static unsigned long long fnv1a_64(const unsigned char* buf, size_t len) {
unsigned long long h = 1469598103934665603ULL;
@@ -2815,7 +2836,10 @@ static void test_config_wire_golden_receive() {
ok = ok && recv->super_mode == SUPER_MODE_ON;
ok = ok && recv->chown_uid == 1234 && recv->chown_gid == 5678;
ok = ok && recv->usermap_count == 2 && recv->usermap[0].from == IDENTITY_MATCH_ANY &&
recv->usermap[0].to == 1000 && recv->usermap[1].from == 5 && recv->usermap[1].to == 6;
recv->usermap[0].from_hi == IDENTITY_MATCH_ANY && recv->usermap[0].to == 1000 &&
recv->usermap[1].from == 5 && recv->usermap[1].from_hi == 9 && recv->usermap[1].to == 6;
ok = ok && recv->groupmap_count == 1 && recv->groupmap[0].from == 7 &&
recv->groupmap[0].to_name != NULL && strcmp(recv->groupmap[0].to_name, "root") == 0;
ok = ok && recv->basis_count == 2 && recv->basis_dirs[0].type == BASIS_DEST_COMPARE &&
recv->basis_dirs[1].type == BASIS_DEST_LINK;
ok = ok && recv->module != NULL && strcmp(recv->module, "goldenmod") == 0;