@@ -686,6 +686,54 @@ fn is_sparse(buf: &[u8]) -> bool {
686686 buf. iter ( ) . all ( |& e| e == 0u8 )
687687}
688688
689+ /// Handle O_DIRECT write errors by temporarily removing the flag and retrying.
690+ /// This follows GNU dd behavior for partial block writes with O_DIRECT.
691+ #[ cfg( any( target_os = "linux" , target_os = "android" ) ) ]
692+ fn handle_o_direct_write ( f : & mut File , buf : & [ u8 ] , original_error : io:: Error ) -> io:: Result < usize > {
693+ use nix:: fcntl:: { FcntlArg , OFlag , fcntl} ;
694+
695+ // Get current flags using nix
696+ let oflags = match fcntl ( & mut * f, FcntlArg :: F_GETFL ) {
697+ Ok ( flags) => OFlag :: from_bits_retain ( flags) ,
698+ Err ( _) => return Err ( original_error) ,
699+ } ;
700+
701+ // If O_DIRECT is set, try removing it temporarily
702+ if oflags. contains ( OFlag :: O_DIRECT ) {
703+ let flags_without_direct = oflags - OFlag :: O_DIRECT ;
704+
705+ // Remove O_DIRECT flag using nix
706+ if fcntl ( & mut * f, FcntlArg :: F_SETFL ( flags_without_direct) ) . is_err ( ) {
707+ return Err ( original_error) ;
708+ }
709+
710+ // Retry the write without O_DIRECT
711+ let write_result = f. write ( buf) ;
712+
713+ // Restore O_DIRECT flag using nix (GNU doesn't restore it, but we'll be safer)
714+ // Log any restoration errors without failing the operation
715+ if let Err ( os_err) = fcntl ( & mut * f, FcntlArg :: F_SETFL ( oflags) ) {
716+ // Just log the error, don't fail the whole operation
717+ show_error ! ( "Failed to restore O_DIRECT flag: {}" , os_err) ;
718+ }
719+
720+ write_result
721+ } else {
722+ // O_DIRECT wasn't set, return original error
723+ Err ( original_error)
724+ }
725+ }
726+
727+ /// Stub for non-Linux platforms - just return the original error.
728+ #[ cfg( not( any( target_os = "linux" , target_os = "android" ) ) ) ]
729+ fn handle_o_direct_write (
730+ _f : & mut File ,
731+ _buf : & [ u8 ] ,
732+ original_error : io:: Error ,
733+ ) -> io:: Result < usize > {
734+ Err ( original_error)
735+ }
736+
689737impl Write for Dest {
690738 fn write ( & mut self , buf : & [ u8 ] ) -> io:: Result < usize > {
691739 match self {
@@ -697,7 +745,21 @@ impl Write for Dest {
697745 f. seek ( SeekFrom :: Current ( seek_amt) ) ?;
698746 Ok ( buf. len ( ) )
699747 }
700- Self :: File ( f, _) => f. write ( buf) ,
748+ Self :: File ( f, _) => {
749+ // Try the write first
750+ match f. write ( buf) {
751+ Ok ( len) => Ok ( len) ,
752+ Err ( e)
753+ if e. kind ( ) == io:: ErrorKind :: InvalidInput
754+ && e. raw_os_error ( ) == Some ( libc:: EINVAL ) =>
755+ {
756+ // This might be an O_DIRECT alignment issue.
757+ // Try removing O_DIRECT temporarily and retry.
758+ handle_o_direct_write ( f, buf, e)
759+ }
760+ Err ( e) => Err ( e) ,
761+ }
762+ }
701763 Self :: Stdout ( stdout) => stdout. write ( buf) ,
702764 #[ cfg( unix) ]
703765 Self :: Fifo ( f) => f. write ( buf) ,
0 commit comments