@@ -43,27 +43,41 @@ pub fn pipe<const SIZE_REQUIRED: bool>(s: usize) -> std::io::Result<(PipeReader,
4343///
4444/// At least one of `source` and `target` must be some sort of pipe.
4545/// To get around this requirement, consider splicing from your source into
46- /// a [`pipe`] and then from the pipe into your target (with `splice_exact `):
46+ /// a [`pipe`] and then from the pipe into your target (with `drain_pipe `):
4747/// this is still very efficient.
4848#[ inline]
4949pub fn splice ( source : & impl AsFd , target : & impl AsFd , len : usize ) -> rustix:: io:: Result < usize > {
5050 rustix:: pipe:: splice ( source, None , target, None , len, SpliceFlags :: empty ( ) )
5151}
5252
53- /// Try to splice `len` bytes from `source ` into `target `.
53+ /// splice `len` bytes from `pipe ` into `dest `.
5454///
55- /// Note that this splice_exact does not provide bytes sent when it failed.
56- /// In the case failed relaying splice via pipe, all content of the pipe
57- /// should be drained by `read` to keep bytes sent accurate.
55+ /// returns Ok(Ok(())) if splice succeed
56+ /// returns Ok(Err(())) if splice failed, but read/write fallback succeed
57+ /// returns Err(e) if read/write fallback failed too
58+ ///
59+ /// Thus, ?.is_err() returns serious error at early stage and checks that splice failed
5860#[ inline]
59- pub fn splice_exact ( source : & impl AsFd , target : & impl AsFd , len : usize ) -> rustix:: io:: Result < ( ) > {
60- let mut left = len;
61- while left > 0 {
62- let written = splice ( source, target, left) ?;
63- debug_assert_ne ! ( written, 0 , "unexpected end of data" ) ;
64- left -= written;
61+ pub fn drain_pipe (
62+ pipe : & PipeReader ,
63+ dest : & impl AsFd ,
64+ len : usize ,
65+ ) -> std:: io:: Result < Result < ( ) , ( ) > > {
66+ debug_assert ! ( len <= MAX_ROOTLESS_PIPE_SIZE , "unexpected RAM usage" ) ;
67+ let mut remaining = len;
68+ while remaining > 0 {
69+ if let Ok ( s) = splice ( pipe, dest, remaining) {
70+ remaining -= s;
71+ } else {
72+ // read/write fallback
73+ // use read_to_end to make pipe empty for the case write failed
74+ let mut drain = Vec :: with_capacity ( remaining) ;
75+ pipe. take ( remaining as u64 ) . read_to_end ( & mut drain) ?;
76+ RawWriter ( & dest) . write_all ( & drain) ?;
77+ return Ok ( Err ( ( ) ) ) ;
78+ }
6579 }
66- Ok ( ( ) )
80+ Ok ( Ok ( ( ) ) )
6781}
6882
6983/// check that source is FUSE
@@ -119,17 +133,7 @@ pub fn splice_unbounded_broker(
119133 match splice ( & source, & pipe_wr, MAX_ROOTLESS_PIPE_SIZE ) {
120134 Ok ( 0 ) => return Ok ( Ok ( ( ) ) ) ,
121135 Ok ( n) => {
122- if splice_exact ( & pipe_rd, dest, n) . is_err ( ) {
123- // If the first splice manages to copy to the intermediate
124- // pipe, but the second splice to stdout fails for some reason
125- // we can recover by copying the data that we have from the
126- // intermediate pipe to stdout using unbuffered read/write. Then
127- // we tell the caller to fall back.
128- // use read_to_end to drain pipe for the case write failed
129- debug_assert ! ( n <= MAX_ROOTLESS_PIPE_SIZE , "unexpected RAM usage" ) ;
130- let mut drain = Vec :: with_capacity ( n) ;
131- pipe_rd. take ( n as u64 ) . read_to_end ( & mut drain) ?;
132- RawWriter ( & dest) . write_all ( & drain) ?;
136+ if drain_pipe ( pipe_rd, dest, n) ?. is_err ( ) {
133137 return Ok ( Err ( ( ) ) ) ;
134138 }
135139 }
@@ -197,19 +201,13 @@ pub fn send_n_bytes(input: impl AsFd, target: impl AsFd, n: u64) -> std::io::Res
197201 Ok ( s) => {
198202 n -= s as u64 ;
199203 bytes_written += s as u64 ;
200- if splice_exact ( & broker_r, & target, s) . is_ok ( ) {
201- if n == 0 {
202- // avoid unnecessary splice for small input
203- break false ;
204- }
205- } else {
206- debug_assert ! ( s <= MAX_ROOTLESS_PIPE_SIZE , "unexpected RAM usage" ) ;
207- // use read_to_end to drain pipe at this fallback for the case write failed
208- let mut drain = Vec :: with_capacity ( s) ;
209- broker_r. take ( s as u64 ) . read_to_end ( & mut drain) ?;
210- RawWriter ( & target) . write_all ( & drain) ?;
204+ if drain_pipe ( broker_r, & target, s) ?. is_err ( ) {
211205 break true ;
212206 }
207+ if n == 0 {
208+ // avoid unnecessary splice for small input
209+ break false ;
210+ }
213211 }
214212 _ => break true ,
215213 }
0 commit comments