]> git.lizzy.rs Git - rust.git/blob - library/std/src/io/copy.rs
f54a9b8b7649b5338ab1944f573ffbd4746c4d7e
[rust.git] / library / std / src / io / copy.rs
1 use crate::io::{self, ErrorKind, Read, Write};
2 use crate::mem::MaybeUninit;
3
4 /// Copies the entire contents of a reader into a writer.
5 ///
6 /// This function will continuously read data from `reader` and then
7 /// write it into `writer` in a streaming fashion until `reader`
8 /// returns EOF.
9 ///
10 /// On success, the total number of bytes that were copied from
11 /// `reader` to `writer` is returned.
12 ///
13 /// If you’re wanting to copy the contents of one file to another and you’re
14 /// working with filesystem paths, see the [`fs::copy`] function.
15 ///
16 /// [`fs::copy`]: crate::fs::copy
17 ///
18 /// # Errors
19 ///
20 /// This function will return an error immediately if any call to [`read`] or
21 /// [`write`] returns an error. All instances of [`ErrorKind::Interrupted`] are
22 /// handled by this function and the underlying operation is retried.
23 ///
24 /// [`read`]: Read::read
25 /// [`write`]: Write::write
26 ///
27 /// # Examples
28 ///
29 /// ```
30 /// use std::io;
31 ///
32 /// fn main() -> io::Result<()> {
33 ///     let mut reader: &[u8] = b"hello";
34 ///     let mut writer: Vec<u8> = vec![];
35 ///
36 ///     io::copy(&mut reader, &mut writer)?;
37 ///
38 ///     assert_eq!(&b"hello"[..], &writer[..]);
39 ///     Ok(())
40 /// }
41 /// ```
42 #[stable(feature = "rust1", since = "1.0.0")]
43 pub fn copy<R: ?Sized, W: ?Sized>(reader: &mut R, writer: &mut W) -> io::Result<u64>
44 where
45     R: Read,
46     W: Write,
47 {
48     #[cfg(any(target_os = "linux", target_os = "android"))]
49     {
50         kernel_copy::copy_spec(reader, writer)
51     }
52
53     #[cfg(not(any(target_os = "linux", target_os = "android")))]
54     generic_copy(reader, writer)
55 }
56
57 pub(crate) fn generic_copy<R: ?Sized, W: ?Sized>(reader: &mut R, writer: &mut W) -> io::Result<u64>
58 where
59     R: Read,
60     W: Write,
61 {
62     let mut buf = MaybeUninit::<[u8; super::DEFAULT_BUF_SIZE]>::uninit();
63     // FIXME: #42788
64     //
65     //   - This creates a (mut) reference to a slice of
66     //     _uninitialized_ integers, which is **undefined behavior**
67     //
68     //   - Only the standard library gets to soundly "ignore" this,
69     //     based on its privileged knowledge of unstable rustc
70     //     internals;
71     unsafe {
72         reader.initializer().initialize(buf.assume_init_mut());
73     }
74
75     let mut written = 0;
76     loop {
77         let len = match reader.read(unsafe { buf.assume_init_mut() }) {
78             Ok(0) => return Ok(written),
79             Ok(len) => len,
80             Err(ref e) if e.kind() == ErrorKind::Interrupted => continue,
81             Err(e) => return Err(e),
82         };
83         writer.write_all(unsafe { &buf.assume_init_ref()[..len] })?;
84         written += len as u64;
85     }
86 }
87
88 #[cfg(any(target_os = "linux", target_os = "android"))]
89 mod kernel_copy {
90
91     use crate::cmp::min;
92     use crate::convert::TryInto;
93     use crate::fs::{File, Metadata};
94     use crate::io::{
95         BufRead, BufReader, BufWriter, Read, Result, StderrLock, StdinLock, StdoutLock, Take, Write,
96     };
97     use crate::mem::ManuallyDrop;
98     use crate::net::TcpStream;
99     use crate::os::unix::fs::FileTypeExt;
100     use crate::os::unix::io::{AsRawFd, FromRawFd, RawFd};
101     use crate::process::{ChildStderr, ChildStdin, ChildStdout};
102
103     pub(super) fn copy_spec<R: Read + ?Sized, W: Write + ?Sized>(
104         read: &mut R,
105         write: &mut W,
106     ) -> Result<u64> {
107         let copier = Copier { read, write };
108         SpecCopy::copy(copier)
109     }
110
111     enum FdMeta {
112         Metadata(Metadata),
113         Socket,
114         Pipe,
115         None,
116     }
117
118     impl FdMeta {
119         fn is_fifo(&self) -> bool {
120             match self {
121                 FdMeta::Metadata(meta) => meta.file_type().is_fifo(),
122                 FdMeta::Socket => false,
123                 FdMeta::Pipe => true,
124                 FdMeta::None => false,
125             }
126         }
127     }
128
129     struct CopyParams(FdMeta, Option<RawFd>);
130
131     struct Copier<'a, 'b, R: Read + ?Sized, W: Write + ?Sized> {
132         pub read: &'a mut R,
133         pub write: &'b mut W,
134     }
135
136     trait SpecCopy {
137         fn copy(self) -> Result<u64>;
138     }
139
140     impl<R: Read + ?Sized, W: Write + ?Sized> SpecCopy for Copier<'_, '_, R, W> {
141         default fn copy(self) -> Result<u64> {
142             super::generic_copy(self.read, self.write)
143         }
144     }
145
146     impl<R: CopyRead, W: CopyWrite> SpecCopy for Copier<'_, '_, R, W> {
147         fn copy(self) -> Result<u64> {
148             let (reader, writer) = (self.read, self.write);
149             let r_cfg = reader.properties();
150             let w_cfg = writer.properties();
151
152             // before direct operations  on file descriptors ensure that all source and sink buffers are emtpy
153             let mut flush = || -> crate::io::Result<u64> {
154                 let bytes = reader.drain_to(writer, u64::MAX)?;
155                 writer.flush()?;
156                 Ok(bytes)
157             };
158
159             match (r_cfg, w_cfg) {
160                 (
161                     CopyParams(FdMeta::Metadata(reader_meta), Some(readfd)),
162                     CopyParams(FdMeta::Metadata(writer_meta), Some(writefd)),
163                 ) if reader_meta.is_file() && writer_meta.is_file() => {
164                     let bytes_flushed = flush()?;
165                     let max_write = reader.min_limit();
166                     let (mut reader, mut writer) =
167                         unsafe { (fd_as_file(readfd), fd_as_file(writefd)) };
168                     let len = reader_meta.len();
169                     crate::sys::fs::copy_regular_files(
170                         &mut reader,
171                         &mut writer,
172                         min(len, max_write),
173                     )
174                     .map(|bytes_copied| bytes_copied + bytes_flushed)
175                 }
176                 (
177                     CopyParams(FdMeta::Metadata(reader_meta), Some(readfd)),
178                     CopyParams(_, Some(writefd)),
179                 ) if reader_meta.is_file() => {
180                     // try sendfile, most modern systems it should work with any target as long as the source is a mmapable file.
181                     // in the rare cases where it's no supported the wrapper function will fall back to a normal copy loop
182                     let bytes_flushed = flush()?;
183                     let (mut reader, mut writer) =
184                         unsafe { (fd_as_file(readfd), fd_as_file(writefd)) };
185                     let len = reader_meta.len();
186                     let max_write = reader.min_limit();
187                     crate::sys::fs::sendfile_splice(
188                         crate::sys::fs::SpliceMode::Sendfile,
189                         &mut reader,
190                         &mut writer,
191                         min(len, max_write),
192                     )
193                         .map(|bytes_sent| bytes_sent + bytes_flushed)
194                 }
195                 (CopyParams(reader_meta, Some(readfd)), CopyParams(writer_meta, Some(writefd)))
196                     if reader_meta.is_fifo() || writer_meta.is_fifo() =>
197                 {
198                     // splice
199                     let bytes_flushed = flush()?;
200                     let max_write = reader.min_limit();
201                     let (mut reader, mut writer) =
202                         unsafe { (fd_as_file(readfd), fd_as_file(writefd)) };
203                     crate::sys::fs::sendfile_splice(
204                         crate::sys::fs::SpliceMode::Splice,
205                         &mut reader,
206                         &mut writer,
207                         max_write,
208                     )
209                     .map(|bytes_sent| bytes_sent + bytes_flushed)
210                 }
211                 _ => super::generic_copy(reader, writer),
212             }
213         }
214     }
215
216     #[rustc_specialization_trait]
217     trait CopyRead: Read {
218         fn drain_to<W: Write>(&mut self, _writer: &mut W, _limit: u64) -> Result<u64> {
219             Ok(0)
220         }
221
222         /// The minimum of the limit of all `Take<_>` wrappers, `u64::MAX` otherwise.
223         /// This method does not account for data `BufReader` buffers and would underreport
224         /// the limit of a `Take<BufReader<Take<_>>>` type. Thus its result is only valid
225         /// after draining the buffers.
226         fn min_limit(&self) -> u64 {
227             u64::MAX
228         }
229
230         fn properties(&self) -> CopyParams;
231     }
232
233     #[rustc_specialization_trait]
234     trait CopyWrite: Write {
235         fn properties(&self) -> CopyParams;
236     }
237
238     impl CopyRead for File {
239         fn properties(&self) -> CopyParams {
240             CopyParams(fd_to_meta(self), Some(self.as_raw_fd()))
241         }
242     }
243
244     impl CopyRead for &File {
245         fn properties(&self) -> CopyParams {
246             CopyParams(fd_to_meta(*self), Some(self.as_raw_fd()))
247         }
248     }
249
250     impl CopyWrite for File {
251         fn properties(&self) -> CopyParams {
252             CopyParams(fd_to_meta(self), Some(self.as_raw_fd()))
253         }
254     }
255
256     impl CopyWrite for &File {
257         fn properties(&self) -> CopyParams {
258             CopyParams(fd_to_meta(*self), Some(self.as_raw_fd()))
259         }
260     }
261
262     impl CopyRead for TcpStream {
263         fn properties(&self) -> CopyParams {
264             // avoid the stat syscall since we can be fairly sure it's a socket
265             CopyParams(FdMeta::Socket, Some(self.as_raw_fd()))
266         }
267     }
268
269     impl CopyRead for &TcpStream {
270         fn properties(&self) -> CopyParams {
271             // avoid the stat syscall since we can be fairly sure it's a socket
272             CopyParams(FdMeta::Socket, Some(self.as_raw_fd()))
273         }
274     }
275
276     impl CopyWrite for TcpStream {
277         fn properties(&self) -> CopyParams {
278             // avoid the stat syscall since we can be fairly sure it's a socket
279             CopyParams(FdMeta::Socket, Some(self.as_raw_fd()))
280         }
281     }
282
283     impl CopyWrite for &TcpStream {
284         fn properties(&self) -> CopyParams {
285             // avoid the stat syscall since we can be fairly sure it's a socket
286             CopyParams(FdMeta::Socket, Some(self.as_raw_fd()))
287         }
288     }
289
290     impl CopyWrite for ChildStdin {
291         fn properties(&self) -> CopyParams {
292             CopyParams(FdMeta::Pipe, Some(self.as_raw_fd()))
293         }
294     }
295
296     impl CopyRead for ChildStdout {
297         fn properties(&self) -> CopyParams {
298             CopyParams(FdMeta::Pipe, Some(self.as_raw_fd()))
299         }
300     }
301
302     impl CopyRead for ChildStderr {
303         fn properties(&self) -> CopyParams {
304             CopyParams(FdMeta::Pipe, Some(self.as_raw_fd()))
305         }
306     }
307
308     impl CopyRead for StdinLock<'_> {
309         fn drain_to<W: Write>(&mut self, writer: &mut W, outer_limit: u64) -> Result<u64> {
310             let buf_reader = self.as_mut_buf();
311             let buf = buf_reader.buffer();
312             let buf = &buf[0..min(buf.len(), outer_limit.try_into().unwrap_or(usize::MAX))];
313             let bytes_drained = buf.len();
314             writer.write_all(buf)?;
315             buf_reader.consume(bytes_drained);
316
317             Ok(bytes_drained as u64)
318         }
319
320         fn properties(&self) -> CopyParams {
321             CopyParams(fd_to_meta(self), Some(self.as_raw_fd()))
322         }
323     }
324
325     impl CopyWrite for StdoutLock<'_> {
326         fn properties(&self) -> CopyParams {
327             CopyParams(fd_to_meta(self), Some(self.as_raw_fd()))
328         }
329     }
330
331     impl CopyWrite for StderrLock<'_> {
332         fn properties(&self) -> CopyParams {
333             CopyParams(fd_to_meta(self), Some(self.as_raw_fd()))
334         }
335     }
336
337     impl<T: CopyRead> CopyRead for Take<T> {
338         fn drain_to<W: Write>(&mut self, writer: &mut W, outer_limit: u64) -> Result<u64> {
339             let local_limit = self.limit();
340             let combined_limit = min(outer_limit, local_limit);
341             let bytes_drained = self.get_mut().drain_to(writer, combined_limit)?;
342             // update limit since read() was bypassed
343             self.set_limit(local_limit - bytes_drained);
344
345             Ok(bytes_drained)
346         }
347
348         fn min_limit(&self) -> u64 {
349             min(Take::limit(self), self.get_ref().min_limit())
350         }
351
352         fn properties(&self) -> CopyParams {
353             self.get_ref().properties()
354         }
355     }
356
357     impl<T: CopyRead> CopyRead for BufReader<T> {
358         fn drain_to<W: Write>(&mut self, writer: &mut W, outer_limit: u64) -> Result<u64> {
359             let buf = self.buffer();
360             let buf = &buf[0..min(buf.len(), outer_limit.try_into().unwrap_or(usize::MAX))];
361             let bytes = buf.len();
362             writer.write_all(buf)?;
363             self.consume(bytes);
364
365             let remaining = outer_limit - bytes as u64;
366
367             // in case of nested bufreaders we also need to drain the ones closer to the source
368             let inner_bytes = self.get_mut().drain_to(writer, remaining)?;
369
370             Ok(bytes as u64 + inner_bytes)
371         }
372
373         fn min_limit(&self) -> u64 {
374             self.get_ref().min_limit()
375         }
376
377         fn properties(&self) -> CopyParams {
378             self.get_ref().properties()
379         }
380     }
381
382     impl<T: CopyWrite> CopyWrite for BufWriter<T> {
383         fn properties(&self) -> CopyParams {
384             self.get_ref().properties()
385         }
386     }
387
388     fn fd_to_meta<T: AsRawFd>(fd: &T) -> FdMeta {
389         let fd = fd.as_raw_fd();
390         let file: ManuallyDrop<File> = ManuallyDrop::new(unsafe { File::from_raw_fd(fd) });
391         match file.metadata() {
392             Ok(meta) => FdMeta::Metadata(meta),
393             Err(_) => FdMeta::None,
394         }
395     }
396
397     unsafe fn fd_as_file(fd: RawFd) -> ManuallyDrop<File> {
398         ManuallyDrop::new(File::from_raw_fd(fd))
399     }
400 }