@@ -6,8 +6,195 @@ use libc::{self, off_t};
66use Result ;
77use errno:: Errno ;
88
9- pub fn sendfile ( out_fd : RawFd , in_fd : RawFd , offset : Option < & mut off_t > , count : usize ) -> Result < usize > {
10- let offset = offset. map ( |offset| offset as * mut _ ) . unwrap_or ( ptr:: null_mut ( ) ) ;
9+ /// Copy up to `count` bytes to `out_fd` from `in_fd` starting at `offset`.
10+ ///
11+ /// Returns a `Result` with the number of bytes written.
12+ ///
13+ /// If `offset` is `None`, `sendfile` will begin reading at the current offset of `in_fd`and will
14+ /// update the offset of `in_fd`. If `offset` is `Some`, `sendfile` will begin at the specified
15+ /// offset and will not update the offset of `in_fd`. Instead, it will mutate `offset` to point to
16+ /// the byte after the last byte copied.
17+ ///
18+ /// `in_fd` must support `mmap`-like operations and therefore cannot be a socket.
19+ ///
20+ /// For more information, see [the sendfile(2) man page.](http://man7.org/linux/man-pages/man2/sendfile.2.html)
21+ #[ cfg( any( target_os = "android" , target_os = "linux" ) ) ]
22+ pub fn sendfile (
23+ out_fd : RawFd ,
24+ in_fd : RawFd ,
25+ offset : Option < & mut off_t > ,
26+ count : usize ,
27+ ) -> Result < usize > {
28+ let offset = offset
29+ . map ( |offset| offset as * mut _ )
30+ . unwrap_or ( ptr:: null_mut ( ) ) ;
1131 let ret = unsafe { libc:: sendfile ( out_fd, in_fd, offset, count) } ;
1232 Errno :: result ( ret) . map ( |r| r as usize )
1333}
34+
35+ cfg_if ! {
36+ if #[ cfg( any( target_os = "freebsd" ,
37+ target_os = "ios" ,
38+ target_os = "macos" ) ) ] {
39+ use sys:: uio:: IoVec ;
40+
41+ #[ allow( missing_debug_implementations) ]
42+ struct SendfileHeaderTrailer <' a>(
43+ libc:: sf_hdtr,
44+ Option <Vec <IoVec <& ' a [ u8 ] >>>,
45+ Option <Vec <IoVec <& ' a [ u8 ] >>>,
46+ ) ;
47+
48+ impl <' a> SendfileHeaderTrailer <' a> {
49+ fn new(
50+ headers: Option <& ' a [ & ' a [ u8 ] ] >,
51+ trailers: Option <& ' a [ & ' a [ u8 ] ] >
52+ ) -> SendfileHeaderTrailer <' a> {
53+ let header_iovecs: Option <Vec <IoVec <& [ u8 ] >>> =
54+ headers. map( |s| s. iter( ) . map( |b| IoVec :: from_slice( b) ) . collect( ) ) ;
55+ let trailer_iovecs: Option <Vec <IoVec <& [ u8 ] >>> =
56+ trailers. map( |s| s. iter( ) . map( |b| IoVec :: from_slice( b) ) . collect( ) ) ;
57+ SendfileHeaderTrailer (
58+ libc:: sf_hdtr {
59+ headers: {
60+ header_iovecs
61+ . as_ref( )
62+ . map_or( ptr:: null( ) , |v| v. as_ptr( ) ) as * mut libc:: iovec
63+ } ,
64+ hdr_cnt: header_iovecs. as_ref( ) . map( |v| v. len( ) ) . unwrap_or( 0 ) as i32 ,
65+ trailers: {
66+ trailer_iovecs
67+ . as_ref( )
68+ . map_or( ptr:: null( ) , |v| v. as_ptr( ) ) as * mut libc:: iovec
69+ } ,
70+ trl_cnt: trailer_iovecs. as_ref( ) . map( |v| v. len( ) ) . unwrap_or( 0 ) as i32
71+ } ,
72+ header_iovecs,
73+ trailer_iovecs,
74+ )
75+ }
76+ }
77+ }
78+ }
79+
80+ cfg_if ! {
81+ if #[ cfg( target_os = "freebsd" ) ] {
82+ use libc:: c_int;
83+
84+ libc_bitflags!{
85+ /// Configuration options for [`sendfile`.](fn.sendfile.html)
86+ pub struct SfFlags : c_int {
87+ /// Causes `sendfile` to return EBUSY instead of blocking when attempting to read a
88+ /// busy page.
89+ SF_NODISKIO ;
90+ /// Causes `sendfile` to sleep until the network stack releases its reference to the
91+ /// VM pages read. When `sendfile` returns, the data is not guaranteed to have been
92+ /// sent, but it is safe to modify the file.
93+ SF_SYNC ;
94+ /// Causes `sendfile` to cache exactly the number of pages specified in the
95+ /// `readahead` parameter, disabling caching heuristics.
96+ SF_USER_READAHEAD ;
97+ /// Causes `sendfile` not to cache the data read.
98+ SF_NOCACHE ;
99+ }
100+ }
101+
102+ /// Read up to `count` bytes from `in_fd` starting at `offset` and write to `out_sock`.
103+ ///
104+ /// Returns a `Result` and a count of bytes written. Bytes written may be non-zero even if
105+ /// an error occurs.
106+ ///
107+ /// `in_fd` must describe a regular file or shared memory object. `out_sock` must describe a
108+ /// stream socket.
109+ ///
110+ /// If `offset` falls past the end of the file, the function returns success and zero bytes
111+ /// written.
112+ ///
113+ /// If `count` is `None` or 0, bytes will be read from `in_fd` until reaching the end of
114+ /// file (EOF).
115+ ///
116+ /// `headers` and `trailers` specify optional slices of byte slices to be sent before and
117+ /// after the data read from `in_fd`, respectively. The length of headers and trailers sent
118+ /// is included in the returned count of bytes written. The values of `offset` and `count`
119+ /// do not apply to headers or trailers.
120+ ///
121+ /// `readahead` specifies the minimum number of pages to cache in memory ahead of the page
122+ /// currently being sent.
123+ ///
124+ /// For more information, see
125+ /// [the sendfile(2) man page.](https://www.freebsd.org/cgi/man.cgi?query=sendfile&sektion=2)
126+ pub fn sendfile(
127+ in_fd: RawFd ,
128+ out_sock: RawFd ,
129+ offset: off_t,
130+ count: Option <usize >,
131+ headers: Option <& [ & [ u8 ] ] >,
132+ trailers: Option <& [ & [ u8 ] ] >,
133+ flags: SfFlags ,
134+ readahead: u16
135+ ) -> ( Result <( ) >, off_t) {
136+ // Readahead goes in upper 16 bits
137+ // Flags goes in lower 16 bits
138+ // see `man 2 sendfile`
139+ let flags: u32 = ( ( readahead as u32 ) << 16 ) | ( flags. bits( ) as u32 ) ;
140+ let mut bytes_sent: off_t = 0 ;
141+ let hdtr = headers. or( trailers) . map( |_| SendfileHeaderTrailer :: new( headers, trailers) ) ;
142+ let hdtr_ptr = hdtr. as_ref( ) . map_or( ptr:: null( ) , |s| & s. 0 as * const libc:: sf_hdtr) ;
143+ let return_code = unsafe {
144+ libc:: sendfile( in_fd,
145+ out_sock,
146+ offset,
147+ count. unwrap_or( 0 ) ,
148+ hdtr_ptr as * mut libc:: sf_hdtr,
149+ & mut bytes_sent as * mut off_t,
150+ flags as c_int)
151+ } ;
152+ ( Errno :: result( return_code) . and( Ok ( ( ) ) ) , bytes_sent)
153+ }
154+ } else if #[ cfg( any( target_os = "ios" , target_os = "macos" ) ) ] {
155+ /// Read bytes from `in_fd` starting at `offset` and write up to `count` bytes to
156+ /// `out_sock`.
157+ ///
158+ /// Returns a `Result` and a count of bytes written. Bytes written may be non-zero even if
159+ /// an error occurs.
160+ ///
161+ /// `in_fd` must describe a regular file. `out_sock` must describe a stream socket.
162+ ///
163+ /// If `offset` falls past the end of the file, the function returns success and zero bytes
164+ /// written.
165+ ///
166+ /// If `count` is `None` or 0, bytes will be read from `in_fd` until reaching the end of
167+ /// file (EOF).
168+ ///
169+ /// `hdtr` specifies an optional list of headers and trailers to be sent before and after
170+ /// the data read from `in_fd`, respectively. The length of headers and trailers sent is
171+ /// included in the returned count of bytes written. If any headers are specified and
172+ /// `count` is non-zero, the length of the headers will be counted in the limit of total
173+ /// bytes sent. Trailers do not count toward the limit of bytes sent and will always be sent
174+ /// regardless. The value of `offset` does not affect headers or trailers.
175+ ///
176+ /// For more information, see
177+ /// [the sendfile(2) man page.](https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man2/sendfile.2.html)
178+ pub fn sendfile(
179+ in_fd: RawFd ,
180+ out_sock: RawFd ,
181+ offset: off_t,
182+ count: Option <off_t>,
183+ headers: Option <& [ & [ u8 ] ] >,
184+ trailers: Option <& [ & [ u8 ] ] >
185+ ) -> ( Result <( ) >, off_t) {
186+ let mut len = count. unwrap_or( 0 ) ;
187+ let hdtr = headers. or( trailers) . map( |_| SendfileHeaderTrailer :: new( headers, trailers) ) ;
188+ let hdtr_ptr = hdtr. as_ref( ) . map_or( ptr:: null( ) , |s| & s. 0 as * const libc:: sf_hdtr) ;
189+ let return_code = unsafe {
190+ libc:: sendfile( in_fd,
191+ out_sock,
192+ offset,
193+ & mut len as * mut off_t,
194+ hdtr_ptr as * mut libc:: sf_hdtr,
195+ 0 )
196+ } ;
197+ ( Errno :: result( return_code) . and( Ok ( ( ) ) ) , len)
198+ }
199+ }
200+ }
0 commit comments