acls/xattrs: -X/--xattrs, -A/--acls, --fake-super
CI / lint (pull_request) Failing after 55s
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

New src/shared/xattr.{c,h}: capture user.* + POSIX ACL xattrs, transmit a bounded
per-file block, re-apply fd-relative. security.*/trusted.*/other system.* never
transmitted/applied (receiver re-validates). Bounds: name<=255 value<=1MiB count
<=256 total<=4MiB. --fake-super records uid:gid:mode:mtime in reserved
user.fastsync.stat (receiver-only). PROTOCOL_VERSION 2.12.0->2.13.0. Review
fixes: reserved key not forwardable, link/hardlink copy-fallback preserves
xattrs, no const-param mutation, per-file warning dedup.
This commit is contained in:
2026-09-08 22:27:53 +02:00
parent 0de859b302
commit 747946c318
23 changed files with 1298 additions and 57 deletions
+24 -2
View File
@@ -153,6 +153,8 @@ static void config_set_defaults(Config* config) {
config->omit_dir_times = false;
config->omit_link_times = false;
config->open_noatime = false;
config->use_xattrs = false;
config->fake_super = false;
}
static bool valid_wire_bool(int value) {
@@ -202,8 +204,11 @@ static bool validate_received_config(const Config* config) {
!((config->append || config->append_verify) && config->use_chunk_serialization) &&
!(config->preserve_hard_links && config->use_chunk_serialization) &&
!(config->preserve_hard_links && (config->append || config->append_verify)) &&
/* The xattr block rides the per-file streaming frame, which -s drops. */
!((config->preserve_xattrs || config->preserve_acls) && config->use_chunk_serialization) &&
valid_wire_bool(config->preserve_atimes) && valid_wire_bool(config->preserve_crtimes) &&
valid_wire_bool(config->omit_dir_times) && valid_wire_bool(config->omit_link_times) &&
valid_wire_bool(config->fake_super) &&
(!config->use_compression ||
(config->compression_level >= 1 && config->compression_level <= 22)) &&
config->chunk_size > 0 && config->chunk_size <= MAX_CHUNK_SIZE &&
@@ -771,6 +776,21 @@ static bool receive_metadata_times_options(int fd, Config* c) {
receive_wire_bool(fd, &c->omit_link_times);
}
/* -X/--xattrs, -A/--acls, --fake-super (Phase-4). The receiver learns
* preserve_xattrs/preserve_acls from the earlier file-options block and
* recomputes the derived use_xattrs there; only --fake-super (receiver-side
* behavior) needs an extra wire bit. Trailing field; protocol 2.13.0. */
static bool send_phase4_xattr_options(int fd, const Config* c) {
return send_int(fd, c->fake_super);
}
static bool receive_phase4_xattr_options(int fd, Config* c) {
if (!receive_wire_bool(fd, &c->fake_super))
return false;
c->use_xattrs = c->preserve_acls || c->preserve_xattrs;
return true;
}
bool config_send(int file_descriptor, const Config* config) {
protocol_session_set_max_alloc(NULL, config->max_alloc);
if (!send_core_fields(file_descriptor, config) || !send_delta_fields(file_descriptor, config) ||
@@ -780,7 +800,8 @@ bool config_send(int file_descriptor, const Config* config) {
!send_basis_options(file_descriptor, config) || !send_fuzzy_option(file_descriptor, config) ||
!send_checksum_options(file_descriptor, config) ||
!send_identity_options(file_descriptor, config) ||
!send_metadata_times_options(file_descriptor, config))
!send_metadata_times_options(file_descriptor, config) ||
!send_phase4_xattr_options(file_descriptor, config))
return false;
Status status;
if (!receive_status(file_descriptor, &status))
@@ -817,7 +838,8 @@ Config* config_receive(int file_descriptor) {
!receive_fuzzy_option(file_descriptor, config) ||
!receive_checksum_options(file_descriptor, config) ||
!receive_identity_options(file_descriptor, config) ||
!receive_metadata_times_options(file_descriptor, config))
!receive_metadata_times_options(file_descriptor, config) ||
!receive_phase4_xattr_options(file_descriptor, config))
goto error;
if (config->compress_choice[0] != '\0' && strcmp(config->compress_choice, "zstd") != 0 &&
strcmp(config->compress_choice, "none") != 0) {