fix(receiver): close re-review findings — dry-run basis oracle, ACL capture, fsync reopen

Follow-up to a237043 addressing three security/correctness re-review findings.

(1) MEDIUM: a server-contacting --dry-run with --compare-dest/--copy-dest/
    --link-dest still read and hashed the basis file and compared it with the
    client-supplied digest, a 1-bit content oracle. basis_match_find() gains a
    hash_content parameter; the dry-run shortcut passes false and returns no
    match without touching basis bytes, so an otherwise-matching entry is
    reported as would-transfer. The real (non-dry-run) path is unchanged.

(2) LOW: xattr_capture_path() hardcoded preserve_acls=true, so the receiver's
    hard-link copy fallback re-applied system.posix_acl_* even when -A was not
    negotiated. The function now takes preserve_acls and members.* is
    unaffected; scanner and receiver callers thread the negotiated flag.

(3) INFO: the --fsync --link-dest temp reopen now uses O_NONBLOCK and treats
    a raced-in FIFO's ENXIO as a benign fsync-skip instead of blocking.

Tests: dry-run + basis unit test (asserts would-transfer, no content read) and
integration test; xattr capture ACL-filter test. Verified strict build, ASan,
clang-format, cppcheck, and the CI integration subset.
This commit is contained in:
2026-09-14 17:19:27 +02:00
parent a2370433b2
commit 5893de4a34
8 changed files with 240 additions and 23 deletions
+7 -4
View File
@@ -65,10 +65,13 @@ bool xattr_list_append(FileXattrList* list, const char* name, const void* value,
* Used for both capture and receiver-side validation. */
bool xattr_name_appliable(const char* name, bool preserve_acls);
/* Sender: read the whitelisted xattrs of `path` into a new list. Returns NULL
* when the path has no appliable xattrs (or the filesystem has no xattr
* support); an empty-but-valid list is never returned distinct from NULL. */
FileXattrList* xattr_capture_path(const char* path);
/* Sender: read the whitelisted xattrs of `path` into a new list. The POSIX ACL
* names are captured only when `preserve_acls` (--acls/-A) is set, so a plain
* -X run never carries an ACL it was not asked to preserve; `user.*` is
* unaffected. Returns NULL when the path has no appliable xattrs (or the
* filesystem has no xattr support); an empty-but-valid list is never returned
* distinct from NULL. */
FileXattrList* xattr_capture_path(const char* path, bool preserve_acls);
/* Wire: bounded serialization. xattr_send returns false on write failure; an
* empty/NULL list transmits a zero-count block. xattr_receive returns NULL and