Commit Graph
899 Commits
Author SHA1 Message Date
TapTap 04514c5b70 Merge feat/p7-output-fs: Phase 7 Wave B (sparse hole preservation, partial retention, fake-super replay, block-size; crtimes/stderr -> Impossible/Divergence) 2026-09-12 10:32:03 +02:00
TapTap a539d8b2ba docs(p7-output-fs): align fake-super comments with silent EPERM/EACCES skip (re-review nit) 2026-09-12 10:31:56 +02:00
TapTap f1a447bb4a feat(p7-times): real directory/symlink time preservation; -O/-J meaningful
Wave D of Phase 7. Make -O/--omit-dir-times and -J/--omit-link-times real by
preserving directory and symlink times, and mark --secluded-args as an explicit
Impossible/Divergence no-op.

Wire: PROTOCOL_VERSION 2.16.0 -> 2.17.0. Adds a terminal STATUS_DIR_TIMES frame
(int count + (wire path, metadata) pairs) sent after all file data and the
optional delete manifest. STATUS_MKDIR also carries metadata for --dirs entries.
Config-frame layout is unchanged.

Sender: the recursive scanner captures every traversed source directory (both
DirectoryScanner and the parallel scanner root + workers, appends mutex-guarded)
into a shared list; the single-threaded and -m paths transmit it last.

Receiver: a DirTimeList accumulates received directory metadata and applies it
with fd-relative no-follow utimensat only at the very end -- after all children,
after the commit-style --delete, and after --delay-updates publication -- in the
single-threaded success frame and in server.c after the -m threads join. -O skips
the application. Symlink metadata is applied at link creation with
utimensat/fchownat/fchmodat AT_SYMLINK_NOFOLLOW; -J suppresses only link times.
identity_apply_ownership_link shares the identity resolver with the fd path.

Docs: -O/-J rows -> Implemented; --secluded-args -> Impossible/Divergence;
--protocol accepted/rejected values and Phase-6/7 notes updated.

Tests: unit (scanner dir capture, DirTimeList apply, symlink metadata, protocol
version values) and integration (dir mtime round-trip + -O, symlink mtime
round-trip + -J, independent suppression), parameterized over single/multithread.
2026-09-12 10:31:20 +02:00
TapTap 227d001092 feat(p7-devices): finalize device/special-file statuses (devices/copy/write -> implemented, specials -> Impossible/Divergence for sockets) + coverage 2026-09-12 10:10:21 +02:00
TapTap f2ba8211ce fix(p7-output-fs): address c-review (fake-super mode sanitization HIGH; sparse/preallocate precedence; partial no_replace guard; stronger tests)
- fake_super_restore_fd now sanitizes mode like metadata_mode (never grants
  S_IWGRP|S_IWOTH; 0666 -> 0644), fixing a privilege regression
- --sparse takes precedence over --preallocate (skip posix_fallocate when
  sparse) so holes are not re-allocated; docs corrected
- --partial retention disabled under --no_replace (ignore/existing) and only
  marks write_attempted after the write begins (no empty-temp retention)
- accept --block-size=SIZE / --delta-block=SIZE inline forms; neutral messages
- fake-super EPERM/EACCES skipped silently (docs aligned); EINVAL still logged
- sparse unit test now memcmp's the full buffer; TestBlockSize integration keeps
  the destination basis so delta is genuinely exercised
- unit 37/37, cppcheck 0, clang-format 0
2026-09-12 09:55:40 +02:00
TapTap 47de05d215 feat(p7-output-fs): sparse hole preservation (-S), partial retention (-P), fake-super replay; block-size verified; crtimes/stderr -> Impossible/Divergence (Wave B)
- write_all_sparse: skips all-zero runs >= 4096 bytes via lseek(SEEK_CUR) and
  ftruncates the final size, wired into the atomic temp+rename and --inplace
  paths with no wire change (full image already in memory).
- --partial retention: on a save failure after the temp held data, rename the
  already-written temp to the destination path (best-effort; falls through to
  unlink; never retains when --partial is off) so --append/--append-verify can
  resume; tested by forcing futimens EINVAL with an out-of-range nsec.
- --block-size aliases --delta-block; verified config->delta_block_size is
  honored by the delta engine end-to-end (unit + integration tests).
- fake_super_restore_fd: parses and re-applies user.fastsync.stat fd-relative
  (fchown best-effort/non-root skipped, fchmod, futimens); a save under
  --fake-super now both records and re-applies.
- -N/--crtimes and --stderr=client promoted to a new 'Impossible/Divergence'
  status bucket (Summary: 136/2/4/3/2 = 147).
- cppcheck/clang-format clean; unit 37/37; integration 407 passed.
2026-09-11 15:58:20 +02:00
TapTap f34eb34f87 Merge feat/p7-cli-namespace: Phase 7 Wave A CLI-namespace parity (renames colliding short flags to rsync parity)
CI / lint (push) Successful in 1m17s
CI / sanitizers (address) (push) Successful in 54s
CI / sanitizers (undefined) (push) Successful in 54s
CI / fuzz-build (push) Successful in 22s
CI / coverage (push) Successful in 46s
CI / valgrind (push) Successful in 38s
CI / build-and-test (push) Successful in 4m30s
2026-09-11 14:44:58 +02:00
TapTap 560ed601f9 docs(p7-cli-namespace): sweep remaining -m-as-multithreading refs to -j/--threads 2026-09-11 14:37:48 +02:00
TapTap d39ddab42c fix(p7-cli-namespace): address c-review (setfacl -m mangled to --threads; two -s tests lost intent; label/doc sweeps)
- revert over-eager replacement of setfacl -m in test_features.py
- use --chunk-serialization (+ set dirs) in the two append/append-verify
  chunk-serialization rejection unit tests so they exercise the real check
- rename archive-negation integration test (--no-preserve is inert under
  archive because devices/specials force metadata)
- update stale (-c)/(-m)/(-s)/(-f) display labels and RSYNC_COMPAT -c/-m refs
- document the --no-perms negation limitation in the Wave A note
2026-09-11 14:34:03 +02:00
TapTap 66e82f9384 feat(p7-cli-namespace): rename colliding short flags to rsync parity (Wave A)
-c -> --checksum, -m -> --prune-empty-dirs, -M -> --remote-option,
-f -> --filter, -s -> --secluded-args, -p -> --perms, -T -> --temp-dir;
-a/--archive is now real rsync -rlptgoD (links+metadata+devices+specials).

FastSync's own flags moved to long-form-only or new shorts:
-j/--threads (multithreading), --preserve (metadata), --sendfile,
--chunk-serialization, --timeout, --ssh-port. Client-side only; the
wire config fields are unchanged (no PROTOCOL_VERSION bump). The server
keeps -p as its port. Docs (README, RSYNC_COMPAT summary 129->132) and
unit/integration tests updated. 37/37 unit, 400-pass integration.
2026-09-11 14:20:15 +02:00
TapTap 15363c0018 docs: add Phase 7 plan (CLI namespace parity, output/filesystem completion, privilege) toward full rsync flag parity 2026-09-11 13:47:44 +02:00
TapTap c1eac6321c docs: recount RSYNC_COMPAT to 129/2 after --batch (Wave D); Phase 6 complete
CI / lint (push) Successful in 1m16s
CI / sanitizers (address) (push) Successful in 57s
CI / sanitizers (undefined) (push) Successful in 55s
CI / fuzz-build (push) Successful in 22s
CI / coverage (push) Successful in 46s
CI / valgrind (push) Successful in 38s
CI / build-and-test (push) Successful in 4m33s
2026-09-10 22:07:10 +02:00
TapTap f43f236f66 Merge feat/p6-batch: residual batch --write-batch/--only-write-batch/--read-batch 2026-09-10 22:06:23 +02:00
TapTap fc2d144492 fix(p6-batch): reject clean EOF on record read; add traversal/EOF security tests 2026-09-10 22:05:18 +02:00
TapTap c026176bb3 feat(p6-batch): residual-batch write/read driver + codec
Implements the client-only residual-batch feature end-to-end:
- src/shared/batch.{c,h}: self-contained single-file batch codec using the
  existing chunk_serialize/chunk_deserialize codec (byte-identical by
  construction).  Magic+format-version header (metadata mode is persisted into
  the header so a batch is self-describing across machines), length-prefixed
  chunk records, bounded reads that reject malformed/truncated/oversized
  records cleanly.
- src/client/client_send.c: write_batch_from_source (deterministic separate
  scan pass, loads every chunk's file images, emits header+records) and
  apply_batch_to_dest (local apply to a destination root via
  file_save_to_disk_full).  No wire change, no server involved.
- src/client/client_validation.c: --write-batch XOR --only-write-batch;
  --read-batch exclusive with both; --read-batch needs only a DEST,
  --only-write-batch only a SOURCE.
- src/client/client_cli.c: main() drives the three batch modes without
  connecting/transferring for read/only-write; --write-batch runs the live
  transfer (single-threaded so the config survives) then emits the batch.
- tests/test_batch.{c,h} (unit: byte-identical roundtrip with and without
  metadata; bad-magic/truncated/oversized rejection) + tests/integration/
  test_batch.py (only-write no-server, read-batch no-source roundtrip,
  --write-batch with a live transfer, conflict rejections).
- clang-format: realign PART-1 config.h comment block.

No PROTOCOL_VERSION bump, no config-frame field, no server flag.
2026-09-10 21:40:07 +02:00
TapTap 4930127312 feat(p6-batch): CLI/config plumbing for --write-batch/--only-write-batch/--read-batch 2026-09-10 21:14:30 +02:00
TapTap 52f45692b9 docs: recount RSYNC_COMPAT to 126/5 after --protocol (Wave C)
CI / lint (push) Successful in 1m15s
CI / sanitizers (undefined) (push) Successful in 54s
CI / sanitizers (address) (push) Successful in 56s
CI / fuzz-build (push) Successful in 21s
CI / coverage (push) Successful in 45s
CI / valgrind (push) Successful in 39s
CI / build-and-test (push) Successful in 4m41s
2026-09-10 20:45:32 +02:00
TapTap 40b0870514 Merge feat/p6-protocol: --protocol version-force flag (client-only) 2026-09-10 20:44:44 +02:00
TapTap 5262cc2597 test(p6-protocol): harden validation null-check; cover missing-arg --protocol 2026-09-10 20:44:33 +02:00
TapTap 9fe6d6c748 test(p6-protocol): unit + integration coverage for --protocol 2026-09-10 20:36:05 +02:00
TapTap 2b7bb2d523 feat(p6-protocol): --protocol version-force flag (client-only) 2026-09-10 20:36:05 +02:00
TapTap bb27b2af50 docs: correct RSYNC_COMPAT selection/update rows to implemented (no code change)
CI / lint (push) Successful in 1m15s
CI / sanitizers (undefined) (push) Successful in 55s
CI / sanitizers (address) (push) Successful in 56s
CI / fuzz-build (push) Successful in 21s
CI / coverage (push) Successful in 44s
CI / valgrind (push) Successful in 38s
CI / build-and-test (push) Successful in 4m43s
2026-09-10 20:16:03 +02:00
TapTap a2ac599298 docs: recount RSYNC_COMPAT to 121/10 after Phase 6 waves A-B (--stop-after/--stop-at, --iconv)
CI / lint (push) Successful in 1m15s
CI / sanitizers (address) (push) Successful in 53s
CI / sanitizers (undefined) (push) Successful in 52s
CI / fuzz-build (push) Successful in 22s
CI / coverage (push) Successful in 45s
CI / valgrind (push) Successful in 38s
CI / build-and-test (push) Successful in 4m40s
2026-09-10 18:18:17 +02:00
TapTap 282aebb7f5 Merge feat/p6-stop: --stop-after/--stop-at deadline stop 2026-09-10 18:16:55 +02:00
TapTap b2afcf2c65 Merge feat/p6-iconv: --iconv charset conversion + PROTOCOL 2.16.0 2026-09-10 18:16:51 +02:00
TapTap 09a07179c9 fix(p6-stop): init -m scan_stopped_early; gate delete warning; stabilize partial-stop test 2026-09-10 18:16:35 +02:00
TapTap cac805b661 fix(p6-iconv): prevent convert buffer overflow; validate both directions; reset on error; more tests 2026-09-10 17:49:29 +02:00
TapTap ac7e9e3bc1 fix(p6-stop): block delete-manifest on early stop; sync -m manifest access; overflow guard 2026-09-10 17:39:34 +02:00
TapTap 7d6665633d test(p6-iconv): iconv unit, config wire-roundtrip, integration tests 2026-09-10 17:13:06 +02:00
TapTap 6e02a24232 feat(p6-iconv): --iconv charset conversion + PROTOCOL 2.16.0 2026-09-10 17:13:01 +02:00
TapTap 37cff96537 test(p6-stop): unit and integration tests for stop deadlines 2026-09-10 16:27:49 +02:00
TapTap 24d1448246 feat(p6-stop): --stop-after/--stop-at deadline transfer stop 2026-09-10 16:27:49 +02:00
TapTap 6bb63c6f21 docs: recount RSYNC_COMPAT to 118/13 after daemon Wave C (--no-motd + MOTD)
CI / lint (push) Successful in 1m14s
CI / sanitizers (address) (push) Successful in 55s
CI / sanitizers (undefined) (push) Successful in 53s
CI / fuzz-build (push) Successful in 21s
CI / coverage (push) Successful in 45s
CI / valgrind (push) Successful in 38s
CI / build-and-test (push) Successful in 5m42s
2026-09-10 13:59:15 +02:00
TapTap 61eb08023a Merge feat/d5-daemon-motd: daemon MOTD display + --no-motd 2026-09-10 13:59:07 +02:00
TapTap 0c35cf2b82 test(d5-daemon-motd): assert no banner when no config key 2026-09-10 13:58:21 +02:00
TapTap cf5f730940 feat(d5-daemon-motd): daemon MOTD display + --no-motd 2026-09-10 13:52:24 +02:00
TapTap 8330f275a6 docs: recount RSYNC_COMPAT to 117/14 after daemon Wave B auth (--password-file, --early-input)
CI / lint (push) Successful in 1m13s
CI / sanitizers (undefined) (push) Successful in 54s
CI / sanitizers (address) (push) Successful in 56s
CI / fuzz-build (push) Successful in 20s
CI / coverage (push) Successful in 44s
CI / valgrind (push) Successful in 38s
CI / build-and-test (push) Successful in 5m10s
2026-09-10 13:27:39 +02:00
TapTap d3c9f1d218 Merge feat/d5-daemon-auth: daemon password auth (--password-file, --early-input) 2026-09-10 13:27:27 +02:00
TapTap accd34ad60 fix(d5-daemon-auth): address auth review findings (Wave B)
- test_credentials.c: NUL-terminate the overlong-line stack buffer before
  make_tmp_file's strlen() (was a stack-buffer-overflow READ under ASan);
  still exercises the overlong-rejection path.
- Add redacted protocol string variants (protocol_send_str_redacted /
  receive + fd send_str_redacted/receive_str_redacted) and use them for the
  daemon auth username/digest so --verbose / LOG_DEBUG_ALL never logs a
  replayable credential while other protocol strings keep their debug trace.
- credentials_verify/gate: replace byte-wise-short-circuiting strcmp with a
  fixed-length constant-time username compare (closes user-enumeration oracle);
  update doc comment to match.
- read_secret_file: preserve password exact bytes (only strip trailing CR/LF)
  and burn the stack line buffer; document the whitespace behavior.
- test_server_cli.c: note the parser zero-inits opts on failure.
- Add debug-level daemon test asserting the digest never appears under --verbose.

PROTOCOL_VERSION stays 2.15.0.
2026-09-10 13:20:42 +02:00
TapTap dd5ae60459 feat(d5-daemon-auth): password auth, --password-file, --early-input 2026-09-10 12:50:56 +02:00
TapTap 958eddf414 docs: recount RSYNC_COMPAT to 115/16 after daemon Wave A (--daemon/--config/--dparam/--no-detach)
CI / lint (push) Successful in 1m12s
CI / sanitizers (undefined) (push) Successful in 54s
CI / sanitizers (address) (push) Successful in 55s
CI / fuzz-build (push) Successful in 22s
CI / coverage (push) Successful in 46s
CI / valgrind (push) Successful in 38s
CI / build-and-test (push) Successful in 4m41s
2026-09-09 18:40:06 +02:00
TapTap 8440dbfdb8 Merge feat/d5-daemon-core: daemon lifecycle, module config, ::dest (PROTOCOL 2.15.0) 2026-09-09 18:39:37 +02:00
TapTap 7c55409a6b fix(d5-daemon-core): cppcheck const-correctness, wire module length cap, daemonize chdir/umask, daemon confinement tests
- daemon_conf.c/server.c/test_daemon_conf.c: const-qualify parse/loop pointers;
  scope user_path static inside its block (clears the 9-wave-A cppcheck findings)
- config.c receive_daemon_module: reject invalid/over-long wire module names
  (> DAEMON_MAX_MODULE_NAME) with a clean STATUS_ERROR; client side already
  enforced via daemon_module_name_valid in config_parse_daemon_dest
- server.c daemonize: chdir(/) and umask(0) so module paths resolve from /
  and config-requested file modes are honored; PROTOCOL_VERSION stays 2.15.0
- test_daemon.py: confinement (read-only/unknown no-write anywhere), module-less
  and dot-dot destination refusal, real daemon_detach double-fork path
2026-09-09 18:30:52 +02:00
TapTap b3d7d64347 feat(d5-daemon-core): daemon lifecycle, module config, ::dest, PROTOCOL 2.15.0 2026-09-09 17:48:07 +02:00
TapTap 9d17e951f1 docs: recount RSYNC_COMPAT to 111/20 after Phase 5 waves A-C; clang-format 18 reflow
CI / lint (push) Successful in 1m9s
CI / sanitizers (undefined) (push) Successful in 53s
CI / sanitizers (address) (push) Successful in 55s
CI / fuzz-build (push) Successful in 19s
CI / coverage (push) Successful in 43s
CI / valgrind (push) Successful in 37s
CI / build-and-test (push) Successful in 4m28s
2026-09-09 14:37:52 +02:00
TapTap 1e02ebcd32 Merge feat/p5-remote-option: --remote-option, --trust-sender
# Conflicts:
#	RSYNC_COMPAT.md
#	src/client/client_cli.c
#	src/client/client_send.c
#	src/shared/transport_ssh.c
#	src/shared/transport_ssh.h
#	tests/integration/test_ssh.py
#	tests/test_client_cli.c
#	tests/test_transport_ssh.c
2026-09-09 14:35:26 +02:00
TapTap b1c63ff947 Merge feat/p5-socket: --address, -4/-6, --sockopts, server bind options
# Conflicts:
#	RSYNC_COMPAT.md
#	tests/test_client_cli.c
2026-09-09 14:30:36 +02:00
TapTap 35f4297538 Merge feat/p5-rsh: --rsh/-e, --rsync-path, --blocking-io, --outbuf 2026-09-09 14:29:58 +02:00
TapTap f86ba7a556 fix(p5-socket): clang-format 18 reflow + cppcheck const-correctness 2026-09-09 14:29:46 +02:00
TapTap cdcaf21acd fix(p5-rsh): NULL-check argv tail str_dups in ssh_build_client_argv 2026-09-09 14:29:46 +02:00