Skip to content

feat(transfer): wire IconvSetting -> FilenameConverter at config build (#1911)#3555

Merged
oferchen merged 3 commits into
masterfrom
feature/iconv-bridge-1911
May 2, 2026
Merged

feat(transfer): wire IconvSetting -> FilenameConverter at config build (#1911)#3555
oferchen merged 3 commits into
masterfrom
feature/iconv-bridge-1911

Conversation

@oferchen
Copy link
Copy Markdown
Owner

@oferchen oferchen commented May 2, 2026

Summary

  • Removes the standalone:iconv-local-ssh entry from tools/ci/known_failures.conf (both KNOWN_FAILURES and DASHBOARD_ENTRIES) now that the IconvSetting -> FilenameConverter bridge is wired through every transfer mode.
  • Per docs/audits/iconv-pipeline.md Finding 4, --iconv was previously parsed by the CLI and forwarded to the remote peer but never converted to a protocol::FilenameConverter for the local process, so file-list reader/writer ran with iconv: None and copied raw bytes.
  • The producer-side wiring is in place across all three transfer modes, so test_iconv_local_ssh should now pass and start gating regressions.

Plumbing path (CLI -> transfer)

CLI --iconv -> IconvSetting::parse -> ClientConfig.iconv -> IconvSetting::resolve_converter() -> Option<FilenameConverter> -> consumed in three places:

  1. SSH push/pull (and embedded SSH): apply_common_server_flags in crates/core/src/client/remote/flags.rs:228 writes server_config.connection.iconv = config.iconv().resolve_converter(). Invoked from build_server_config_for_receiver and build_server_config_for_generator in both ssh_transfer.rs and embedded_ssh_transfer.rs.
  2. Daemon push/pull: same apply_common_server_flags is also called from daemon_transfer/orchestration/server_config.rs (both directions). The daemon-side counterpart lives at crates/daemon/src/daemon/sections/module_access/client_args.rs:283, which resolves the module's charset = directive into the same connection.iconv slot (PR feat(daemon): wire module charset= directive to iconv runtime (#1917) #3554).
  3. Local copy: LocalCopyOptionsBuilder::apply_iconv at crates/core/src/client/run/mod.rs:399 writes the converter onto LocalCopyOptions::iconv, since the local-copy executor in the engine crate does not traverse the SSH/daemon ServerConfig builder.

The transfer crate consumes the converter at:

  • crates/transfer/src/receiver/mod.rs:369 -> reader.with_iconv(...)
  • crates/transfer/src/generator/mod.rs:570 -> writer.with_iconv(...)

Both feed the protocol crate's FileListReader / FileListWriter, where #1912 and #1913 already apply the converter via apply_encoding_conversion on every emitted and ingested filename.

KNOWN_FAILURES removal

  • Dropped standalone:iconv-local-ssh from the KNOWN_FAILURES=( ... ) array.
  • Dropped the matching dashboard line from DASHBOARD_ENTRIES=( ... ).
  • Left standalone:delta-stats and standalone:upstream-compressed-batch-self-roundtrip untouched.

Upstream references

  • flist.c:1579-1603 - send_file_entry() filename iconvbufs(ic_send, ...)
  • flist.c:738-754 - recv_file_entry() filename iconvbufs(ic_recv, ...)
  • options.c::recv_iconv_settings - parses --iconv=LOCAL,REMOTE
  • compat.c:716-718 - gates CF_SYMLINK_ICONV on iconv configured

Test plan

  • CI: fmt + clippy pass.
  • CI: nextest (stable) passes.
  • CI: Windows / macOS / Linux musl pass.
  • Interop: test_iconv_local_ssh_interop runs and passes against upstream rsync 3.4.1 (was previously masked by the KF entry; now gating regressions).
  • Interop: test_iconv and test_iconv_upstream_interop continue to pass.

#1911)

Per docs/audits/iconv-pipeline.md Finding 4, --iconv was previously a
no-op end to end: the CLI parsed --iconv into IconvSetting and forwarded
the value to the remote peer, but the local process never converted that
setting into a protocol::FilenameConverter, so the file-list reader and
writer ran with iconv: None and copied raw bytes through transfer.

The bridge is now in place across all three transfer modes:

- SSH push/pull (and embedded SSH): apply_common_server_flags writes
  `server_config.connection.iconv = config.iconv().resolve_converter()`
  in crates/core/src/client/remote/flags.rs:228, invoked from
  build_server_config_for_receiver and build_server_config_for_generator
  (ssh_transfer.rs and embedded_ssh_transfer.rs).
- Daemon push/pull: apply_common_server_flags is also called from
  daemon_transfer/orchestration/server_config.rs (both paths). On the
  daemon side, module_access/client_args.rs:283 resolves the module's
  `charset =` directive into the same FilenameConverter slot.
- Local copy: LocalCopyOptionsBuilder::apply_iconv in
  crates/core/src/client/run/mod.rs:399 writes the converter onto
  LocalCopyOptions::iconv, since the local-copy executor in the engine
  crate bypasses the SSH/daemon ServerConfig builder.

The transfer crate consumes the converter at:

- crates/transfer/src/receiver/mod.rs:369 - reader.with_iconv(...)
- crates/transfer/src/generator/mod.rs:570 - writer.with_iconv(...)

These hand the converter to the protocol crate's FileListReader /
FileListWriter, where #1912 and #1913 already apply it via
apply_encoding_conversion on every emitted and ingested filename. With
the producer-side wiring landed, the test_iconv_local_ssh interop
scenario starts gating regressions, so the standalone:iconv-local-ssh
entry is removed from KNOWN_FAILURES and DASHBOARD_ENTRIES.

Upstream references:

- flist.c:1579-1603 send_file_entry filename iconvbufs(ic_send, ...)
- flist.c:738-754 recv_file_entry filename iconvbufs(ic_recv, ...)
- options.c::recv_iconv_settings parses --iconv=LOCAL,REMOTE
@github-actions github-actions Bot added the enhancement New feature or request label May 2, 2026
oferchen added 2 commits May 2, 2026 11:10
PR #3458 (cd76ec2) wired IconvSetting -> FilenameConverter for the
local generator/receiver, but two architectural gaps prevent the
SSH iconv scenario from passing end-to-end:

- --iconv is not forwarded to the spawned remote rsync. Upstream
  options.c:2715-2723 forwards the post-comma half of iconv_opt so
  the remote setup_iconv() agrees on charsets.
- FilenameConverter (crates/protocol/src/iconv/converter.rs) treats
  remote_encoding as the literal wire charset. Upstream rsync.c:87-147
  always uses UTF-8 on the wire and treats `charset` as each peer's
  local charset.

Audit findings 1-3 and 5 (docs/audits/iconv-pipeline.md) also remain
open: symlink target transcoding, --files-from line forwarding,
secluded-args argv re-encoding, CF_SYMLINK_ICONV gating.

Restoring the KF entry until the iconv pipeline rework lands.
@oferchen oferchen merged commit daf0849 into master May 2, 2026
41 checks passed
@oferchen oferchen deleted the feature/iconv-bridge-1911 branch May 2, 2026 13:01
oferchen added a commit that referenced this pull request May 5, 2026
#1911) (#3555)

* feat(transfer): wire IconvSetting -> FilenameConverter at config build (#1911)

Per docs/audits/iconv-pipeline.md Finding 4, --iconv was previously a
no-op end to end: the CLI parsed --iconv into IconvSetting and forwarded
the value to the remote peer, but the local process never converted that
setting into a protocol::FilenameConverter, so the file-list reader and
writer ran with iconv: None and copied raw bytes through transfer.

The bridge is now in place across all three transfer modes:

- SSH push/pull (and embedded SSH): apply_common_server_flags writes
  `server_config.connection.iconv = config.iconv().resolve_converter()`
  in crates/core/src/client/remote/flags.rs:228, invoked from
  build_server_config_for_receiver and build_server_config_for_generator
  (ssh_transfer.rs and embedded_ssh_transfer.rs).
- Daemon push/pull: apply_common_server_flags is also called from
  daemon_transfer/orchestration/server_config.rs (both paths). On the
  daemon side, module_access/client_args.rs:283 resolves the module's
  `charset =` directive into the same FilenameConverter slot.
- Local copy: LocalCopyOptionsBuilder::apply_iconv in
  crates/core/src/client/run/mod.rs:399 writes the converter onto
  LocalCopyOptions::iconv, since the local-copy executor in the engine
  crate bypasses the SSH/daemon ServerConfig builder.

The transfer crate consumes the converter at:

- crates/transfer/src/receiver/mod.rs:369 - reader.with_iconv(...)
- crates/transfer/src/generator/mod.rs:570 - writer.with_iconv(...)

These hand the converter to the protocol crate's FileListReader /
FileListWriter, where #1912 and #1913 already apply it via
apply_encoding_conversion on every emitted and ingested filename. With
the producer-side wiring landed, the test_iconv_local_ssh interop
scenario starts gating regressions, so the standalone:iconv-local-ssh
entry is removed from KNOWN_FAILURES and DASHBOARD_ENTRIES.

Upstream references:

- flist.c:1579-1603 send_file_entry filename iconvbufs(ic_send, ...)
- flist.c:738-754 recv_file_entry filename iconvbufs(ic_recv, ...)
- options.c::recv_iconv_settings parses --iconv=LOCAL,REMOTE

* revert(ci): restore standalone:iconv-local-ssh known failure

PR #3458 (cd76ec2) wired IconvSetting -> FilenameConverter for the
local generator/receiver, but two architectural gaps prevent the
SSH iconv scenario from passing end-to-end:

- --iconv is not forwarded to the spawned remote rsync. Upstream
  options.c:2715-2723 forwards the post-comma half of iconv_opt so
  the remote setup_iconv() agrees on charsets.
- FilenameConverter (crates/protocol/src/iconv/converter.rs) treats
  remote_encoding as the literal wire charset. Upstream rsync.c:87-147
  always uses UTF-8 on the wire and treats `charset` as each peer's
  local charset.

Audit findings 1-3 and 5 (docs/audits/iconv-pipeline.md) also remain
open: symlink target transcoding, --files-from line forwarding,
secluded-args argv re-encoding, CF_SYMLINK_ICONV gating.

Restoring the KF entry until the iconv pipeline rework lands.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant