Skip to content

fix(fuse): drain pipe on splice failure and handle ECONNABORTED#1299

Merged
szbr9486 merged 1 commit into
CurvineIO:mainfrom
yangcx000:fix/fuse-splice-drain-econnaborted
Jul 25, 2026
Merged

fix(fuse): drain pipe on splice failure and handle ECONNABORTED#1299
szbr9486 merged 1 commit into
CurvineIO:mainfrom
yangcx000:fix/fuse-splice-drain-econnaborted

Conversation

@yangcx000

Copy link
Copy Markdown
Collaborator

What

Hardens the FUSE splice receive path against partial/failed frames and connection-abort errno.

  • splice() — on short splice, read_buf error, or read/write length mismatch, drain the pipe before returning so stale bytes never poison the next request frame.
    • On the recoverable read_buf-error path (the returned err carries a real OS errno, so the receive loop may continue) the drain is load-bearing.
    • On the two fatal-exit paths (short splice / length mismatch) the returned err_box! carries no OS errno, so the loop hits _ => return Err and the receiver exits — there is no "next frame". The drain there is defensive (documented inline) in case those paths later become recoverable.
  • drain_pipe() helper — relies on the non-blocking fuse pipe (PipeFd::new(_, false, false)): an empty pipe returns EAGAIN (not EINTR), which stops the loop, so it cannot hang. Retries only EINTR.
  • prepare_receive_buf() helper — centralizes buffer clear()/reserve()/set_len(). The added clear() makes the buffer length deterministic before the unsafe set_len, a small correctness improvement over the previous in-place set_len.
  • ECONNABORTED — treated like ENODEV in the receive loop (graceful break), with a matching errno label and receive_error_labels test coverage. Both graceful-exit arms now log an info! line to distinguish a normal teardown from a hang in the field.
  • Short-read/short-splice/length-mismatch detail is folded into the returned err_box! (logged once, at error level, when it propagates to fuse_session::start) rather than an extra error! at the site.

Testing

  • cargo fmt -p curvine-fuse -- --check
  • cargo clippy -p curvine-fuse --lib (clean)
  • cargo test -p curvine-fuse --libfuse_receiver:: + fuse_error:: all pass

🤖 Generated with Claude Code

Harden the FUSE receive path against partial/failed splice frames and
connection-abort errno:

- splice(): on short splice, read_buf error, or read/write length
  mismatch, drain the pipe before returning so a poisoned pipe never
  leaks stale bytes into the next request frame. On the recoverable
  read_buf-error path this is load-bearing; on the two fatal-exit paths
  the drain is defensive (documented inline) in case they later become
  recoverable.
- Add drain_pipe() helper (relies on the non-blocking fuse pipe: an
  empty pipe returns EAGAIN and stops the loop) and prepare_receive_buf()
  to centralize buffer reset/reserve/set_len.
- Treat ECONNABORTED like ENODEV in the receive loop (graceful break),
  add its errno label, and cover it in receive_error_labels/tests. Log
  an info! line on both graceful-exit arms to distinguish a normal
  teardown from a hang in the field.
- Fold short-read/short-splice/length-mismatch detail into the returned
  err_box! (logged once, at error level, when it propagates to
  fuse_session::start) instead of an extra error! at the site.

Co-Authored-By: Claude <noreply@anthropic.com>
@yangcx000
yangcx000 requested review from bigbigxu and szbr9486 July 24, 2026 16:37
@szbr9486
szbr9486 merged commit c233541 into CurvineIO:main Jul 25, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants