@@ -22,9 +22,12 @@ use crate::cmp;
2222use crate :: io:: { self , BorrowedCursor , IoSlice , IoSliceMut , Read } ;
2323use crate :: os:: unix:: io:: { AsFd , AsRawFd , BorrowedFd , FromRawFd , IntoRawFd , OwnedFd , RawFd } ;
2424use crate :: sys:: cvt;
25- #[ cfg( all( target_os = "android" , target_pointer_width = "64" ) ) ]
25+ #[ cfg( all( any ( target_os = "android" , target_os = "linux" ) , target_pointer_width = "64" ) ) ]
2626use crate :: sys:: pal:: weak:: syscall;
27- #[ cfg( any( all( target_os = "android" , target_pointer_width = "32" ) , target_vendor = "apple" ) ) ]
27+ #[ cfg( any(
28+ all( any( target_os = "android" , target_os = "linux" ) , target_pointer_width = "32" ) ,
29+ target_vendor = "apple"
30+ ) ) ]
2831use crate :: sys:: pal:: weak:: weak;
2932use crate :: sys_common:: { AsInner , FromInner , IntoInner } ;
3033
@@ -384,6 +387,16 @@ impl FileDesc {
384387 ) ) ]
385388 use libc:: pwrite64;
386389
390+ // Work around linux deviating from POSIX where it ignores the
391+ // offset of pwrite when the file was opened with O_APPEND.
392+ #[ cfg( any( target_os = "linux" , target_os = "android" ) ) ]
393+ {
394+ let iov = [ IoSlice :: new ( buf) ] ;
395+ if let Some ( ret) = self . pwritev2 ( & iov, offset) {
396+ return ret;
397+ }
398+ }
399+
387400 unsafe {
388401 cvt ( pwrite64 (
389402 self . as_raw_fd ( ) ,
@@ -408,6 +421,13 @@ impl FileDesc {
408421 target_os = "openbsd" , // OpenBSD 2.7
409422 ) ) ]
410423 pub fn write_vectored_at ( & self , bufs : & [ IoSlice < ' _ > ] , offset : u64 ) -> io:: Result < usize > {
424+ // Work around linux deviating from POSIX where it ignores the
425+ // offset of pwrite when the file was opened with O_APPEND.
426+ #[ cfg( any( target_os = "linux" , target_os = "android" ) ) ]
427+ if let Some ( ret) = self . pwritev2 ( bufs, offset) {
428+ return ret;
429+ }
430+
411431 let ret = cvt ( unsafe {
412432 libc:: pwritev (
413433 self . as_raw_fd ( ) ,
@@ -611,6 +631,58 @@ impl FileDesc {
611631 pub fn duplicate ( & self ) -> io:: Result < FileDesc > {
612632 Ok ( Self ( self . 0 . try_clone ( ) ?) )
613633 }
634+
635+ #[ cfg( any( target_os = "linux" , target_os = "android" ) ) ]
636+ fn pwritev2 ( & self , bufs : & [ IoSlice < ' _ > ] , offset : u64 ) -> Option < io:: Result < usize > > {
637+ #[ cfg( target_pointer_width = "64" ) ]
638+ syscall ! (
639+ fn pwritev2(
640+ fd: libc:: c_int,
641+ iovec: * const libc:: iovec,
642+ n_iovec: libc:: c_int,
643+ offset: off64_t,
644+ flags: libc:: c_int,
645+ ) -> isize ;
646+ ) ;
647+ #[ cfg( target_pointer_width = "32" ) ]
648+ weak ! (
649+ fn pwritev2(
650+ fd: libc:: c_int,
651+ iovec: * const libc:: iovec,
652+ n_iovec: libc:: c_int,
653+ offset: off64_t,
654+ flags: libc:: c_int,
655+ ) -> isize ;
656+ ) ;
657+
658+ use core:: sync:: atomic:: AtomicBool ;
659+
660+ static NOAPPEND_SUPPORTED : AtomicBool = AtomicBool :: new ( true ) ;
661+ if NOAPPEND_SUPPORTED . load ( core:: sync:: atomic:: Ordering :: Relaxed ) {
662+ let r = unsafe {
663+ cvt ( pwritev2 (
664+ self . as_raw_fd ( ) ,
665+ bufs. as_ptr ( ) as * const libc:: iovec ,
666+ cmp:: min ( bufs. len ( ) , max_iov ( ) ) as libc:: c_int ,
667+ offset as off64_t ,
668+ libc:: RWF_NOAPPEND ,
669+ ) )
670+ } ;
671+ match r {
672+ Ok ( ret) => return Some ( Ok ( ret as usize ) ) ,
673+ Err ( e)
674+ if let Some ( err) = e. raw_os_error ( )
675+ && ( err == libc:: EOPNOTSUPP || err == libc:: ENOSYS ) =>
676+ {
677+ NOAPPEND_SUPPORTED . store ( false , core:: sync:: atomic:: Ordering :: Relaxed ) ;
678+ return None ;
679+ }
680+ Err ( e) => return Some ( Err ( e) ) ,
681+ }
682+ }
683+
684+ return None ;
685+ }
614686}
615687
616688impl < ' a > Read for & ' a FileDesc {
0 commit comments