@@ -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,64 @@ 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+ let pwritev2 = {
649+ weak ! (
650+ fn pwritev2(
651+ fd: libc:: c_int,
652+ iovec: * const libc:: iovec,
653+ n_iovec: libc:: c_int,
654+ offset: off64_t,
655+ flags: libc:: c_int,
656+ ) -> isize ;
657+ ) ;
658+ let Some ( pwritev2) = pwritev2. get ( ) else {
659+ return None ;
660+ } ;
661+ pwritev2
662+ } ;
663+
664+ use core:: sync:: atomic:: AtomicBool ;
665+
666+ static NOAPPEND_SUPPORTED : AtomicBool = AtomicBool :: new ( true ) ;
667+ if NOAPPEND_SUPPORTED . load ( core:: sync:: atomic:: Ordering :: Relaxed ) {
668+ let r = unsafe {
669+ cvt ( pwritev2 (
670+ self . as_raw_fd ( ) ,
671+ bufs. as_ptr ( ) as * const libc:: iovec ,
672+ cmp:: min ( bufs. len ( ) , max_iov ( ) ) as libc:: c_int ,
673+ offset as off64_t ,
674+ libc:: RWF_NOAPPEND ,
675+ ) )
676+ } ;
677+ match r {
678+ Ok ( ret) => return Some ( Ok ( ret as usize ) ) ,
679+ Err ( e)
680+ if let Some ( err) = e. raw_os_error ( )
681+ && ( err == libc:: EOPNOTSUPP || err == libc:: ENOSYS ) =>
682+ {
683+ NOAPPEND_SUPPORTED . store ( false , core:: sync:: atomic:: Ordering :: Relaxed ) ;
684+ return None ;
685+ }
686+ Err ( e) => return Some ( Err ( e) ) ,
687+ }
688+ }
689+
690+ return None ;
691+ }
614692}
615693
616694impl < ' a > Read for & ' a FileDesc {
0 commit comments