@@ -6,8 +6,195 @@ use libc::{self, off_t};
6
6
use Result ;
7
7
use errno:: Errno ;
8
8
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 ( ) ) ;
11
31
let ret = unsafe { libc:: sendfile ( out_fd, in_fd, offset, count) } ;
12
32
Errno :: result ( ret) . map ( |r| r as usize )
13
33
}
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