1 // Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
13 use os
::unix
::prelude
::*;
15 use ffi
::{CString, CStr, OsString, OsStr}
;
17 use io
::{self, Error, ErrorKind, SeekFrom}
;
18 use libc
::{self, c_int, mode_t}
;
20 use path
::{Path, PathBuf}
;
23 use sys
::fd
::FileDesc
;
24 use sys
::time
::SystemTime
;
25 use sys
::{cvt, cvt_r}
;
26 use sys_common
::{AsInner, FromInner}
;
28 #[cfg(any(target_os = "linux", target_os = "emscripten"))]
29 use libc
::{stat64, fstat64, lstat64, off64_t, ftruncate64, lseek64, dirent64, readdir64_r, open64}
;
30 #[cfg(target_os = "android")]
31 use libc
::{stat
as stat64
, fstat
as fstat64
, lstat
as lstat64
, off64_t
, ftruncate64
, lseek64
,
32 dirent
as dirent64
, open
as open64
};
33 #[cfg(not(any(target_os = "linux",
34 target_os
= "emscripten",
35 target_os
= "android")))]
36 use libc
::{stat
as stat64
, fstat
as fstat64
, lstat
as lstat64
, off_t
as off64_t
,
37 ftruncate
as ftruncate64
, lseek
as lseek64
, dirent
as dirent64
, open
as open64
};
38 #[cfg(not(any(target_os = "linux",
39 target_os
= "emscripten",
40 target_os
= "solaris")))]
41 use libc
::{readdir_r as readdir64_r}
;
43 pub struct File(FileDesc
);
55 struct Dir(*mut libc
::DIR
);
57 unsafe impl Send
for Dir {}
58 unsafe impl Sync
for Dir {}
63 // We need to store an owned copy of the directory name
64 // on Solaris because a) it uses a zero-length array to
65 // store the name, b) its lifetime between readdir calls
67 #[cfg(target_os = "solaris")]
72 pub struct OpenOptions
{
85 #[derive(Clone, PartialEq, Eq, Debug)]
86 pub struct FilePermissions { mode: mode_t }
88 #[derive(Copy, Clone, PartialEq, Eq, Hash)]
89 pub struct FileType { mode: mode_t }
91 pub struct DirBuilder { mode: mode_t }
94 pub fn size(&self) -> u64 { self.stat.st_size as u64 }
95 pub fn perm(&self) -> FilePermissions
{
96 FilePermissions { mode: (self.stat.st_mode as mode_t) & 0o777 }
99 pub fn file_type(&self) -> FileType
{
100 FileType { mode: self.stat.st_mode as mode_t }
104 #[cfg(any(target_os = "ios", target_os = "macos"))]
105 // FIXME: update SystemTime to store a timespec and don't lose precision
107 pub fn modified(&self) -> io
::Result
<SystemTime
> {
108 Ok(SystemTime
::from(libc
::timeval
{
109 tv_sec
: self.stat
.st_mtime
,
110 tv_usec
: (self.stat
.st_mtime_nsec
/ 1000) as libc
::suseconds_t
,
114 pub fn accessed(&self) -> io
::Result
<SystemTime
> {
115 Ok(SystemTime
::from(libc
::timeval
{
116 tv_sec
: self.stat
.st_atime
,
117 tv_usec
: (self.stat
.st_atime_nsec
/ 1000) as libc
::suseconds_t
,
121 pub fn created(&self) -> io
::Result
<SystemTime
> {
122 Ok(SystemTime
::from(libc
::timeval
{
123 tv_sec
: self.stat
.st_birthtime
,
124 tv_usec
: (self.stat
.st_birthtime_nsec
/ 1000) as libc
::suseconds_t
,
129 #[cfg(target_os = "netbsd")]
131 pub fn modified(&self) -> io
::Result
<SystemTime
> {
132 Ok(SystemTime
::from(libc
::timespec
{
133 tv_sec
: self.stat
.st_mtime
as libc
::time_t
,
134 tv_nsec
: self.stat
.st_mtimensec
as libc
::c_long
,
138 pub fn accessed(&self) -> io
::Result
<SystemTime
> {
139 Ok(SystemTime
::from(libc
::timespec
{
140 tv_sec
: self.stat
.st_atime
as libc
::time_t
,
141 tv_nsec
: self.stat
.st_atimensec
as libc
::c_long
,
145 pub fn created(&self) -> io
::Result
<SystemTime
> {
146 Ok(SystemTime
::from(libc
::timespec
{
147 tv_sec
: self.stat
.st_birthtime
as libc
::time_t
,
148 tv_nsec
: self.stat
.st_birthtimensec
as libc
::c_long
,
153 #[cfg(not(any(target_os = "ios", target_os = "macos", target_os = "netbsd")))]
155 pub fn modified(&self) -> io
::Result
<SystemTime
> {
156 Ok(SystemTime
::from(libc
::timespec
{
157 tv_sec
: self.stat
.st_mtime
as libc
::time_t
,
158 tv_nsec
: self.stat
.st_mtime_nsec
as libc
::c_long
,
162 pub fn accessed(&self) -> io
::Result
<SystemTime
> {
163 Ok(SystemTime
::from(libc
::timespec
{
164 tv_sec
: self.stat
.st_atime
as libc
::time_t
,
165 tv_nsec
: self.stat
.st_atime_nsec
as libc
::c_long
,
169 #[cfg(any(target_os = "bitrig",
170 target_os
= "freebsd",
171 target_os
= "openbsd"))]
172 pub fn created(&self) -> io
::Result
<SystemTime
> {
173 Ok(SystemTime
::from(libc
::timespec
{
174 tv_sec
: self.stat
.st_birthtime
as libc
::time_t
,
175 tv_nsec
: self.stat
.st_birthtime_nsec
as libc
::c_long
,
179 #[cfg(not(any(target_os = "bitrig",
180 target_os
= "freebsd",
181 target_os
= "openbsd")))]
182 pub fn created(&self) -> io
::Result
<SystemTime
> {
183 Err(io
::Error
::new(io
::ErrorKind
::Other
,
184 "creation time is not available on this platform \
189 impl AsInner
<stat64
> for FileAttr
{
190 fn as_inner(&self) -> &stat64 { &self.stat }
193 impl FilePermissions
{
194 pub fn readonly(&self) -> bool { self.mode & 0o222 == 0 }
195 pub fn set_readonly(&mut self, readonly
: bool
) {
202 pub fn mode(&self) -> u32 { self.mode as u32 }
206 pub fn is_dir(&self) -> bool { self.is(libc::S_IFDIR) }
207 pub fn is_file(&self) -> bool { self.is(libc::S_IFREG) }
208 pub fn is_symlink(&self) -> bool { self.is(libc::S_IFLNK) }
210 pub fn is(&self, mode
: mode_t
) -> bool { self.mode & libc::S_IFMT == mode }
213 impl FromInner
<u32> for FilePermissions
{
214 fn from_inner(mode
: u32) -> FilePermissions
{
215 FilePermissions { mode: mode as mode_t }
219 impl Iterator
for ReadDir
{
220 type Item
= io
::Result
<DirEntry
>;
222 #[cfg(target_os = "solaris")]
223 fn next(&mut self) -> Option
<io
::Result
<DirEntry
>> {
226 // Although readdir_r(3) would be a correct function to use here because
227 // of the thread safety, on Illumos the readdir(3C) function is safe to use
228 // in threaded applications and it is generally preferred over the
229 // readdir_r(3C) function.
230 let entry_ptr
= libc
::readdir(self.dirp
.0);
231 if entry_ptr
.is_null() {
235 let name
= (*entry_ptr
).d_name
.as_ptr();
236 let namelen
= libc
::strlen(name
) as usize;
240 name
: ::slice
::from_raw_parts(name
as *const u8,
241 namelen
as usize).to_owned().into_boxed_slice(),
242 root
: self.root
.clone()
244 if ret
.name_bytes() != b
"." && ret
.name_bytes() != b
".." {
251 #[cfg(not(target_os = "solaris"))]
252 fn next(&mut self) -> Option
<io
::Result
<DirEntry
>> {
254 let mut ret
= DirEntry
{
255 entry
: mem
::zeroed(),
256 root
: self.root
.clone()
258 let mut entry_ptr
= ptr
::null_mut();
260 if readdir64_r(self.dirp
.0, &mut ret
.entry
, &mut entry_ptr
) != 0 {
261 return Some(Err(Error
::last_os_error()))
263 if entry_ptr
.is_null() {
266 if ret
.name_bytes() != b
"." && ret
.name_bytes() != b
".." {
276 let r
= unsafe { libc::closedir(self.0) }
;
277 debug_assert_eq
!(r
, 0);
282 pub fn path(&self) -> PathBuf
{
283 self.root
.join(OsStr
::from_bytes(self.name_bytes()))
286 pub fn file_name(&self) -> OsString
{
287 OsStr
::from_bytes(self.name_bytes()).to_os_string()
290 pub fn metadata(&self) -> io
::Result
<FileAttr
> {
294 #[cfg(target_os = "solaris")]
295 pub fn file_type(&self) -> io
::Result
<FileType
> {
296 stat(&self.path()).map(|m
| m
.file_type())
299 #[cfg(not(target_os = "solaris"))]
300 pub fn file_type(&self) -> io
::Result
<FileType
> {
301 match self.entry
.d_type
{
302 libc
::DT_CHR
=> Ok(FileType { mode: libc::S_IFCHR }
),
303 libc
::DT_FIFO
=> Ok(FileType { mode: libc::S_IFIFO }
),
304 libc
::DT_LNK
=> Ok(FileType { mode: libc::S_IFLNK }
),
305 libc
::DT_REG
=> Ok(FileType { mode: libc::S_IFREG }
),
306 libc
::DT_SOCK
=> Ok(FileType { mode: libc::S_IFSOCK }
),
307 libc
::DT_DIR
=> Ok(FileType { mode: libc::S_IFDIR }
),
308 libc
::DT_BLK
=> Ok(FileType { mode: libc::S_IFBLK }
),
309 _
=> lstat(&self.path()).map(|m
| m
.file_type()),
313 #[cfg(any(target_os = "macos",
316 target_os
= "emscripten",
317 target_os
= "android",
318 target_os
= "solaris"))]
319 pub fn ino(&self) -> u64 {
320 self.entry
.d_ino
as u64
323 #[cfg(any(target_os = "freebsd",
324 target_os
= "openbsd",
325 target_os
= "bitrig",
326 target_os
= "netbsd",
327 target_os
= "dragonfly"))]
328 pub fn ino(&self) -> u64 {
329 self.entry
.d_fileno
as u64
332 #[cfg(any(target_os = "macos",
334 target_os
= "netbsd",
335 target_os
= "openbsd",
336 target_os
= "freebsd",
337 target_os
= "dragonfly",
338 target_os
= "bitrig"))]
339 fn name_bytes(&self) -> &[u8] {
341 ::slice
::from_raw_parts(self.entry
.d_name
.as_ptr() as *const u8,
342 self.entry
.d_namlen
as usize)
345 #[cfg(any(target_os = "android",
347 target_os
= "emscripten"))]
348 fn name_bytes(&self) -> &[u8] {
350 CStr
::from_ptr(self.entry
.d_name
.as_ptr()).to_bytes()
353 #[cfg(target_os = "solaris")]
354 fn name_bytes(&self) -> &[u8] {
360 pub fn new() -> OpenOptions
{
375 pub fn read(&mut self, read
: bool
) { self.read = read; }
376 pub fn write(&mut self, write
: bool
) { self.write = write; }
377 pub fn append(&mut self, append
: bool
) { self.append = append; }
378 pub fn truncate(&mut self, truncate
: bool
) { self.truncate = truncate; }
379 pub fn create(&mut self, create
: bool
) { self.create = create; }
380 pub fn create_new(&mut self, create_new
: bool
) { self.create_new = create_new; }
382 pub fn custom_flags(&mut self, flags
: i32) { self.custom_flags = flags; }
383 pub fn mode(&mut self, mode
: u32) { self.mode = mode as mode_t; }
385 fn get_access_mode(&self) -> io
::Result
<c_int
> {
386 match (self.read
, self.write
, self.append
) {
387 (true, false, false) => Ok(libc
::O_RDONLY
),
388 (false, true, false) => Ok(libc
::O_WRONLY
),
389 (true, true, false) => Ok(libc
::O_RDWR
),
390 (false, _
, true) => Ok(libc
::O_WRONLY
| libc
::O_APPEND
),
391 (true, _
, true) => Ok(libc
::O_RDWR
| libc
::O_APPEND
),
392 (false, false, false) => Err(Error
::from_raw_os_error(libc
::EINVAL
)),
396 fn get_creation_mode(&self) -> io
::Result
<c_int
> {
397 match (self.write
, self.append
) {
400 if self.truncate
|| self.create
|| self.create_new
{
401 return Err(Error
::from_raw_os_error(libc
::EINVAL
));
404 if self.truncate
&& !self.create_new
{
405 return Err(Error
::from_raw_os_error(libc
::EINVAL
));
409 Ok(match (self.create
, self.truncate
, self.create_new
) {
410 (false, false, false) => 0,
411 (true, false, false) => libc
::O_CREAT
,
412 (false, true, false) => libc
::O_TRUNC
,
413 (true, true, false) => libc
::O_CREAT
| libc
::O_TRUNC
,
414 (_
, _
, true) => libc
::O_CREAT
| libc
::O_EXCL
,
420 pub fn open(path
: &Path
, opts
: &OpenOptions
) -> io
::Result
<File
> {
421 let path
= cstr(path
)?
;
422 File
::open_c(&path
, opts
)
425 pub fn open_c(path
: &CStr
, opts
: &OpenOptions
) -> io
::Result
<File
> {
426 let flags
= libc
::O_CLOEXEC
|
427 opts
.get_access_mode()?
|
428 opts
.get_creation_mode()?
|
429 (opts
.custom_flags
as c_int
& !libc
::O_ACCMODE
);
430 let fd
= cvt_r(|| unsafe {
431 open64(path
.as_ptr(), flags
, opts
.mode
as c_int
)
433 let fd
= FileDesc
::new(fd
);
435 // Currently the standard library supports Linux 2.6.18 which did not
436 // have the O_CLOEXEC flag (passed above). If we're running on an older
437 // Linux kernel then the flag is just ignored by the OS, so we continue
438 // to explicitly ask for a CLOEXEC fd here.
440 // The CLOEXEC flag, however, is supported on versions of OSX/BSD/etc
441 // that we support, so we only do this on Linux currently.
442 if cfg
!(target_os
= "linux") {
449 pub fn file_attr(&self) -> io
::Result
<FileAttr
> {
450 let mut stat
: stat64
= unsafe { mem::zeroed() }
;
452 fstat64(self.0.raw(), &mut stat
)
454 Ok(FileAttr { stat: stat }
)
457 pub fn fsync(&self) -> io
::Result
<()> {
458 cvt_r(|| unsafe { libc::fsync(self.0.raw()) }
)?
;
462 pub fn datasync(&self) -> io
::Result
<()> {
463 cvt_r(|| unsafe { os_datasync(self.0.raw()) }
)?
;
466 #[cfg(any(target_os = "macos", target_os = "ios"))]
467 unsafe fn os_datasync(fd
: c_int
) -> c_int
{
468 libc
::fcntl(fd
, libc
::F_FULLFSYNC
)
470 #[cfg(target_os = "linux")]
471 unsafe fn os_datasync(fd
: c_int
) -> c_int { libc::fdatasync(fd) }
472 #[cfg(not(any(target_os = "macos",
474 target_os
= "linux")))]
475 unsafe fn os_datasync(fd
: c_int
) -> c_int { libc::fsync(fd) }
478 pub fn truncate(&self, size
: u64) -> io
::Result
<()> {
480 ftruncate64(self.0.raw(), size
as off64_t
)
485 pub fn read(&self, buf
: &mut [u8]) -> io
::Result
<usize> {
489 pub fn read_to_end(&self, buf
: &mut Vec
<u8>) -> io
::Result
<usize> {
490 self.0.read_to_end(buf
)
493 pub fn write(&self, buf
: &[u8]) -> io
::Result
<usize> {
497 pub fn flush(&self) -> io
::Result
<()> { Ok(()) }
499 pub fn seek(&self, pos
: SeekFrom
) -> io
::Result
<u64> {
500 let (whence
, pos
) = match pos
{
501 SeekFrom
::Start(off
) => (libc
::SEEK_SET
, off
as off64_t
),
502 SeekFrom
::End(off
) => (libc
::SEEK_END
, off
as off64_t
),
503 SeekFrom
::Current(off
) => (libc
::SEEK_CUR
, off
as off64_t
),
505 let n
= cvt(unsafe { lseek64(self.0.raw(), pos, whence) }
)?
;
509 pub fn duplicate(&self) -> io
::Result
<File
> {
510 self.0.duplicate().map(File
)
513 pub fn fd(&self) -> &FileDesc { &self.0 }
515 pub fn into_fd(self) -> FileDesc { self.0 }
519 pub fn new() -> DirBuilder
{
520 DirBuilder { mode: 0o777 }
523 pub fn mkdir(&self, p
: &Path
) -> io
::Result
<()> {
525 cvt(unsafe { libc::mkdir(p.as_ptr(), self.mode) }
)?
;
529 pub fn set_mode(&mut self, mode
: u32) {
530 self.mode
= mode
as mode_t
;
534 fn cstr(path
: &Path
) -> io
::Result
<CString
> {
535 Ok(CString
::new(path
.as_os_str().as_bytes())?
)
538 impl FromInner
<c_int
> for File
{
539 fn from_inner(fd
: c_int
) -> File
{
540 File(FileDesc
::new(fd
))
544 impl fmt
::Debug
for File
{
545 fn fmt(&self, f
: &mut fmt
::Formatter
) -> fmt
::Result
{
546 #[cfg(target_os = "linux")]
547 fn get_path(fd
: c_int
) -> Option
<PathBuf
> {
548 use string
::ToString
;
549 let mut p
= PathBuf
::from("/proc/self/fd");
550 p
.push(&fd
.to_string());
554 #[cfg(target_os = "macos")]
555 fn get_path(fd
: c_int
) -> Option
<PathBuf
> {
556 // FIXME: The use of PATH_MAX is generally not encouraged, but it
557 // is inevitable in this case because OS X defines `fcntl` with
558 // `F_GETPATH` in terms of `MAXPATHLEN`, and there are no
559 // alternatives. If a better method is invented, it should be used
561 let mut buf
= vec
![0;libc
::PATH_MAX
as usize];
562 let n
= unsafe { libc::fcntl(fd, libc::F_GETPATH, buf.as_ptr()) }
;
566 let l
= buf
.iter().position(|&c
| c
== 0).unwrap();
567 buf
.truncate(l
as usize);
569 Some(PathBuf
::from(OsString
::from_vec(buf
)))
572 #[cfg(not(any(target_os = "linux", target_os = "macos")))]
573 fn get_path(_fd
: c_int
) -> Option
<PathBuf
> {
574 // FIXME(#24570): implement this for other Unix platforms
578 #[cfg(any(target_os = "linux", target_os = "macos"))]
579 fn get_mode(fd
: c_int
) -> Option
<(bool
, bool
)> {
580 let mode
= unsafe { libc::fcntl(fd, libc::F_GETFL) }
;
584 match mode
& libc
::O_ACCMODE
{
585 libc
::O_RDONLY
=> Some((true, false)),
586 libc
::O_RDWR
=> Some((true, true)),
587 libc
::O_WRONLY
=> Some((false, true)),
592 #[cfg(not(any(target_os = "linux", target_os = "macos")))]
593 fn get_mode(_fd
: c_int
) -> Option
<(bool
, bool
)> {
594 // FIXME(#24570): implement this for other Unix platforms
598 let fd
= self.0.raw();
599 let mut b
= f
.debug_struct("File");
601 if let Some(path
) = get_path(fd
) {
602 b
.field("path", &path
);
604 if let Some((read
, write
)) = get_mode(fd
) {
605 b
.field("read", &read
).field("write", &write
);
611 pub fn readdir(p
: &Path
) -> io
::Result
<ReadDir
> {
612 let root
= Arc
::new(p
.to_path_buf());
615 let ptr
= libc
::opendir(p
.as_ptr());
617 Err(Error
::last_os_error())
619 Ok(ReadDir { dirp: Dir(ptr), root: root }
)
624 pub fn unlink(p
: &Path
) -> io
::Result
<()> {
626 cvt(unsafe { libc::unlink(p.as_ptr()) }
)?
;
630 pub fn rename(old
: &Path
, new
: &Path
) -> io
::Result
<()> {
631 let old
= cstr(old
)?
;
632 let new
= cstr(new
)?
;
633 cvt(unsafe { libc::rename(old.as_ptr(), new.as_ptr()) }
)?
;
637 pub fn set_perm(p
: &Path
, perm
: FilePermissions
) -> io
::Result
<()> {
639 cvt_r(|| unsafe { libc::chmod(p.as_ptr(), perm.mode) }
)?
;
643 pub fn rmdir(p
: &Path
) -> io
::Result
<()> {
645 cvt(unsafe { libc::rmdir(p.as_ptr()) }
)?
;
649 pub fn remove_dir_all(path
: &Path
) -> io
::Result
<()> {
650 let filetype
= lstat(path
)?
.file_type();
651 if filetype
.is_symlink() {
654 remove_dir_all_recursive(path
)
658 fn remove_dir_all_recursive(path
: &Path
) -> io
::Result
<()> {
659 for child
in readdir(path
)?
{
661 if child
.file_type()?
.is_dir() {
662 remove_dir_all_recursive(&child
.path())?
;
664 unlink(&child
.path())?
;
670 pub fn readlink(p
: &Path
) -> io
::Result
<PathBuf
> {
671 let c_path
= cstr(p
)?
;
672 let p
= c_path
.as_ptr();
674 let mut buf
= Vec
::with_capacity(256);
677 let buf_read
= cvt(unsafe {
678 libc
::readlink(p
, buf
.as_mut_ptr() as *mut _
, buf
.capacity() as libc
::size_t
)
681 unsafe { buf.set_len(buf_read); }
683 if buf_read
!= buf
.capacity() {
686 return Ok(PathBuf
::from(OsString
::from_vec(buf
)));
689 // Trigger the internal buffer resizing logic of `Vec` by requiring
690 // more space than the current capacity. The length is guaranteed to be
691 // the same as the capacity due to the if statement above.
696 pub fn symlink(src
: &Path
, dst
: &Path
) -> io
::Result
<()> {
697 let src
= cstr(src
)?
;
698 let dst
= cstr(dst
)?
;
699 cvt(unsafe { libc::symlink(src.as_ptr(), dst.as_ptr()) }
)?
;
703 pub fn link(src
: &Path
, dst
: &Path
) -> io
::Result
<()> {
704 let src
= cstr(src
)?
;
705 let dst
= cstr(dst
)?
;
706 cvt(unsafe { libc::link(src.as_ptr(), dst.as_ptr()) }
)?
;
710 pub fn stat(p
: &Path
) -> io
::Result
<FileAttr
> {
712 let mut stat
: stat64
= unsafe { mem::zeroed() }
;
714 stat64(p
.as_ptr(), &mut stat
as *mut _
as *mut _
)
716 Ok(FileAttr { stat: stat }
)
719 pub fn lstat(p
: &Path
) -> io
::Result
<FileAttr
> {
721 let mut stat
: stat64
= unsafe { mem::zeroed() }
;
723 lstat64(p
.as_ptr(), &mut stat
as *mut _
as *mut _
)
725 Ok(FileAttr { stat: stat }
)
728 pub fn canonicalize(p
: &Path
) -> io
::Result
<PathBuf
> {
729 let path
= CString
::new(p
.as_os_str().as_bytes())?
;
732 let r
= libc
::realpath(path
.as_ptr(), ptr
::null_mut());
734 return Err(io
::Error
::last_os_error())
736 buf
= CStr
::from_ptr(r
).to_bytes().to_vec();
737 libc
::free(r
as *mut _
);
739 Ok(PathBuf
::from(OsString
::from_vec(buf
)))
742 pub fn copy(from
: &Path
, to
: &Path
) -> io
::Result
<u64> {
743 use fs
::{File, set_permissions}
;
745 return Err(Error
::new(ErrorKind
::InvalidInput
,
746 "the source path is not an existing regular file"))
749 let mut reader
= File
::open(from
)?
;
750 let mut writer
= File
::create(to
)?
;
751 let perm
= reader
.metadata()?
.permissions();
753 let ret
= io
::copy(&mut reader
, &mut writer
)?
;
754 set_permissions(to
, perm
)?
;