@@ -699,6 +699,10 @@ pub trait FileExt {
699
699
700
700
/// Update timestamps (both access and modification) to the current time.
701
701
fn update_timestamps ( & self ) -> io:: Result < ( ) > ;
702
+
703
+ /// Read the exact number of bytes required to fill `buf` starting from `position`,
704
+ /// without affecting file offset.
705
+ fn pread_exact ( & self , buf : & mut [ u8 ] , position : usize ) -> io:: Result < ( ) > ;
702
706
}
703
707
704
708
impl FileExt for File {
@@ -777,6 +781,41 @@ impl FileExt for File {
777
781
} ) ;
778
782
retry_eintr ! ( futimens( self . as_raw_fd( ) , & now, & now, ) . map_err( map_nix_error) )
779
783
}
784
+
785
+ fn pread_exact ( & self , buf : & mut [ u8 ] , start_pos : usize ) -> io:: Result < ( ) > {
786
+ use nix:: sys:: uio:: pread;
787
+
788
+ if buf. len ( ) == 0 {
789
+ return Err ( io:: Error :: new (
790
+ io:: ErrorKind :: InvalidInput ,
791
+ "zero-sized buffer in input" ,
792
+ ) ) ;
793
+ }
794
+
795
+ let mut total_bytes_read = 0 ;
796
+ while total_bytes_read < buf. len ( ) {
797
+ let remaining_buf = & mut buf[ total_bytes_read..] ;
798
+ let cur_offset = start_pos. saturating_add ( total_bytes_read) ;
799
+ let bytes_read =
800
+ retry_eintr ! (
801
+ pread( self . as_raw_fd( ) , remaining_buf, cur_offset as libc:: off_t)
802
+ . map_err( map_nix_error)
803
+ ) ?;
804
+ total_bytes_read += bytes_read;
805
+ if bytes_read == 0 {
806
+ break ;
807
+ }
808
+ }
809
+
810
+ if total_bytes_read < buf. len ( ) {
811
+ return Err ( io:: Error :: new (
812
+ io:: ErrorKind :: UnexpectedEof ,
813
+ format ! ( "pread reached EOF after {} bytes" , total_bytes_read) ,
814
+ ) ) ;
815
+ }
816
+
817
+ Ok ( ( ) )
818
+ }
780
819
}
781
820
782
821
fn to_cstr < P : openat:: AsPath > ( path : P ) -> io:: Result < P :: Buffer > {
@@ -1042,4 +1081,37 @@ mod tests {
1042
1081
assert_eq ! ( & srcbuf, b"test content" ) ;
1043
1082
assert_eq ! ( & srcbuf, & destbuf) ;
1044
1083
}
1084
+
1085
+ #[ test]
1086
+ fn test_pread_exact ( ) {
1087
+ let td = tempfile:: tempdir ( ) . unwrap ( ) ;
1088
+ let d = openat:: Dir :: open ( td. path ( ) ) . unwrap ( ) ;
1089
+ static TESTINPUT : & str = "test1 test2 test3" ;
1090
+ d. write_file_contents ( "foo" , 0o700 , TESTINPUT ) . unwrap ( ) ;
1091
+ let mut testfile = d. open_file ( "foo" ) . unwrap ( ) ;
1092
+ {
1093
+ let mut buf = [ 0 ; 0 ] ;
1094
+ let _ = testfile. pread_exact ( & mut buf, 0 ) . unwrap_err ( ) ;
1095
+ }
1096
+ {
1097
+ let mut buf = [ 0 ; 18 ] ;
1098
+ let _ = testfile. pread_exact ( & mut buf, 0 ) . unwrap_err ( ) ;
1099
+ }
1100
+ {
1101
+ let mut buf = [ 0 ; 1 ] ;
1102
+ let _ = testfile. pread_exact ( & mut buf, 2000 ) . unwrap_err ( ) ;
1103
+ }
1104
+ {
1105
+ let mut buf1 = [ 0 ; 5 ] ;
1106
+ let mut buf2 = [ 0 ; 5 ] ;
1107
+ let _ = testfile. pread_exact ( & mut buf1, 6 ) . unwrap ( ) ;
1108
+ let _ = testfile. pread_exact ( & mut buf2, 6 ) . unwrap ( ) ;
1109
+ assert_eq ! ( buf1, "test2" . as_bytes( ) ) ;
1110
+ assert_eq ! ( buf1, buf2) ;
1111
+
1112
+ let mut str_buf = String :: new ( ) ;
1113
+ let _ = testfile. read_to_string ( & mut str_buf) . unwrap ( ) ;
1114
+ assert_eq ! ( str_buf, TESTINPUT ) ;
1115
+ }
1116
+ }
1045
1117
}
0 commit comments