pub fn copy_file_sparse(from: &Path, to: &Path) -> io::Result<u64> {
    use cvt::*;
    use fs::OpenOptions;
    use io::{ErrorKind, Read};
    use libc::{ftruncate64, lseek64};
    use std::os::unix::{
        fs::{OpenOptionsExt, PermissionsExt},
        io::AsRawFd,
    };

    unsafe fn copy_file_range(
        fd_in: libc::c_int,
        off_in: *mut libc::loff_t,
        fd_out: libc::c_int,
        off_out: *mut libc::loff_t,
        len: libc::size_t,
        flags: libc::c_uint,
    ) -> libc::c_long {
        libc::syscall(
            libc::SYS_copy_file_range,
            fd_in,
            off_in,
            fd_out,
            off_out,
            len,
            flags,
        )
    }

    let mut reader = File::open(from)?;

    let (mode, len) = {
        let metadata = reader.metadata()?;
        if !metadata.is_file() {
            return Err(Error::new(
                ErrorKind::InvalidInput,
                "the source path is not an existing regular file",
            ));
        }
        (metadata.permissions().mode(), metadata.len())
    };

    let bytes_to_copy: i64 = len as i64;

    let mut writer = OpenOptions::new()
        // prevent world readable/writeable file in case of empty umask
        .mode(0o000)
        .write(true)
        .create(true)
        .truncate(true)
        .open(to)?;

    let fd_in = reader.as_raw_fd();
    let fd_out = writer.as_raw_fd();

    cvt_r(|| unsafe { libc::fchmod(fd_out, mode) })?;
    match cvt_r(|| unsafe { ftruncate64(fd_out, bytes_to_copy) }) {
        Ok(_) => {}
        Err(err) => return Err(err),
    };

    let mut srcpos: i64 = 0;

    let (mut can_handle_sparse, mut next_beg) = {
        let ret = unsafe { lseek64(fd_in, srcpos, libc::SEEK_DATA) };
        if ret == -1 {
            (false, 0)
        } else {
            (true, ret)
        }
    };

    let mut next_end: libc::loff_t = bytes_to_copy;
    if can_handle_sparse {
        let ret = unsafe { lseek64(fd_in, next_beg, libc::SEEK_HOLE) };
        if ret == -1 {
            can_handle_sparse = false;
        } else {
            next_end = ret
        }
    }

    let mut next_len = next_end - next_beg;
    let mut use_copy_file_range = true;

    while srcpos < bytes_to_copy {
        if srcpos != 0 {
            if can_handle_sparse {
                next_beg = match cvt(unsafe { lseek64(fd_in, srcpos, libc::SEEK_DATA) }) {
                    Ok(beg) => beg,
                    Err(err) => match err.raw_os_error() {
                        Some(libc::ENXIO) => {
                            // Remaining portion is hole
                            return Ok(srcpos as u64);
                        }
                        _ => {
                            return Err(err);
                        }
                    },
                };

                next_end = cvt(unsafe { lseek64(fd_in, next_beg, libc::SEEK_HOLE) })?;
                next_len = next_end - next_beg;
            } else {
                next_beg = srcpos;
                next_end = bytes_to_copy - srcpos;
            }
        }

        if next_len <= 0 {
            srcpos = next_end;
            continue;
        }

        let num = if use_copy_file_range {
            match cvt(unsafe {
                copy_file_range(
                    fd_in,
                    &mut next_beg,
                    fd_out,
                    &mut next_beg,
                    next_len as usize,
                    0,
                )
            }) {
                Ok(n) => n as isize,
                Err(err) => match err.raw_os_error() {
                    // Try fallback if either:
                    // - Kernel version is < 4.5 (ENOSYS)
                    // - Files are mounted on different fs (EXDEV)
                    // - copy_file_range is disallowed, for example by seccomp (EPERM)
                    Some(libc::ENOSYS) | Some(libc::EPERM) => {
                        use_copy_file_range = false;
                        continue;
                    }
                    Some(libc::EXDEV) | Some(libc::EINVAL) => {
                        use_copy_file_range = false;
                        continue;
                    }
                    _ => {
                        return Err(err);
                    }
                },
            }
        } else {
            if can_handle_sparse {
                cvt(unsafe { lseek64(fd_in, next_beg, libc::SEEK_SET) })?;
                if next_beg != 0 {
                    cvt(unsafe { lseek64(fd_out, next_beg, libc::SEEK_SET) })?;
                }
            }
            const DEFAULT_BUF_SIZE: usize = 16 * 1024;
            let mut buf = unsafe {
                let buf: [u8; DEFAULT_BUF_SIZE] = std::mem::zeroed();
                buf
            };

            let mut written = 0;
            while next_len > 0 {
                let slice_len = next_len.min(DEFAULT_BUF_SIZE as i64) as usize;
                let len = match reader.read(&mut buf[..slice_len]) {
                    Ok(0) => {
                        // break early out of copy loop, because nothing is to be read anymore
                        srcpos += written;
                        break;
                    }
                    Ok(len) => len,
                    Err(ref err) if err.kind() == ErrorKind::Interrupted => continue,
                    Err(err) => return Err(err),
                };
                writer.write_all(&buf[..len])?;
                written += len as i64;
                next_len -= len as i64;
            }
            written as isize
        };
        srcpos += num as i64;
    }

    Ok(srcpos as u64)
}
/// Copies only valid regions of file preserving the sparseness
/// of the file. Also utilizes copy_file_range which performs
/// in_kernel copy without the additional cost of transferring data
/// from the kernel to user space and then back into the kernel. Also
/// on certain file systems that support COW (btrfs/zfs), copy_file_range
/// is a metadata operation and is extremely efficient   
Copies only valid regions of file preserving the sparseness
of the file. Also utilizes copy_file_range which performs
in_kernel copy without the additional cost of transferring data
from the kernel to user space and then back into the kernel. Also
on certain file systems that support COW (btrfs/zfs), copy_file_range
is a metadata operation and is extremely efficient
Copies only valid regions of file preserving the sparseness of the file . Also utilizes copy_file_range which performs in_kernel copy without the additional cost of transferring data from the kernel to user space and then back into the kernel . Also on certain file systems that support COW ( btrfs / zfs ) copy_file_range is a metadata operation and is extremely efficient
pub fn copy_file_sparse(from: &Path, to: &Path) -> io::Result<u64> {
    use cvt::*;
    use fs::OpenOptions;
    use io::{ErrorKind, Read};
    use libc::{ftruncate64, lseek64};
    use std::os::unix::{
        fs::{OpenOptionsExt, PermissionsExt},
        io::AsRawFd,
    };
    unsafe fn copy_file_range(
        fd_in: libc::c_int,
        off_in: *mut libc::loff_t,
        fd_out: libc::c_int,
        off_out: *mut libc::loff_t,
        len: libc::size_t,
        flags: libc::c_uint,
    ) -> libc::c_long {
        libc::syscall(
            libc::SYS_copy_file_range,
            fd_in,
            off_in,
            fd_out,
            off_out,
            len,
            flags,
        )
    }
    let mut reader = File::open(from)?;
    let (mode, len) = {
        let metadata = reader.metadata()?;
        if !metadata.is_file() {
            return Err(Error::new(
                ErrorKind::InvalidInput,
                "the source path is not an existing regular file",
            ));
        }
        (metadata.permissions().mode(), metadata.len())
    };
    let bytes_to_copy: i64 = len as i64;
    let mut writer = OpenOptions::new()
        // prevent world readable/writeable file in case of empty umask
        .mode(0o000)
        .write(true)
        .create(true)
        .truncate(true)
        .open(to)?;
    let fd_in = reader.as_raw_fd();
    let fd_out = writer.as_raw_fd();
    cvt_r(|| unsafe { libc::fchmod(fd_out, mode) })?;
    match cvt_r(|| unsafe { ftruncate64(fd_out, bytes_to_copy) }) {
        Ok(_) => {}
        Err(err) => return Err(err),
    };
    let mut srcpos: i64 = 0;
    let (mut can_handle_sparse, mut next_beg) = {
        let ret = unsafe { lseek64(fd_in, srcpos, libc::SEEK_DATA) };
        if ret == -1 {
            (false, 0)
        } else {
            (true, ret)
        }
    };
    let mut next_end: libc::loff_t = bytes_to_copy;
    if can_handle_sparse {
        let ret = unsafe { lseek64(fd_in, next_beg, libc::SEEK_HOLE) };
        if ret == -1 {
            can_handle_sparse = false;
        } else {
            next_end = ret
        }
    }
    let mut next_len = next_end - next_beg;
    let mut use_copy_file_range = true;
    while srcpos < bytes_to_copy {
        if srcpos != 0 {
            if can_handle_sparse {
                next_beg = match cvt(unsafe { lseek64(fd_in, srcpos, libc::SEEK_DATA) }) {
                    Ok(beg) => beg,
                    Err(err) => match err.raw_os_error() {
                        Some(libc::ENXIO) => {
                            // Remaining portion is hole
                            return Ok(srcpos as u64);
                        }
                        _ => {
                            return Err(err);
                        }
                    },
                };
                next_end = cvt(unsafe { lseek64(fd_in, next_beg, libc::SEEK_HOLE) })?;
                next_len = next_end - next_beg;
            } else {
                next_beg = srcpos;
                next_end = bytes_to_copy - srcpos;
            }
        }
        if next_len <= 0 {
            srcpos = next_end;
            continue;
        }
        let num = if use_copy_file_range {
            match cvt(unsafe {
                copy_file_range(
                    fd_in,
                    &mut next_beg,
                    fd_out,
                    &mut next_beg,
                    next_len as usize,
                    0,
                )
            }) {
                Ok(n) => n as isize,
                Err(err) => match err.raw_os_error() {
                    // Try fallback if either:
                    // - Kernel version is < 4.5 (ENOSYS)
                    // - Files are mounted on different fs (EXDEV)
                    // - copy_file_range is disallowed, for example by seccomp (EPERM)
                    Some(libc::ENOSYS) | Some(libc::EPERM) => {
                        use_copy_file_range = false;
                        continue;
                    }
                    Some(libc::EXDEV) | Some(libc::EINVAL) => {
                        use_copy_file_range = false;
                        continue;
                    }
                    _ => {
                        return Err(err);
                    }
                },
            }
        } else {
            if can_handle_sparse {
                cvt(unsafe { lseek64(fd_in, next_beg, libc::SEEK_SET) })?;
                if next_beg != 0 {
                    cvt(unsafe { lseek64(fd_out, next_beg, libc::SEEK_SET) })?;
                }
            }
            const DEFAULT_BUF_SIZE: usize = 16 * 1024;
            let mut buf = unsafe {
                let buf: [u8; DEFAULT_BUF_SIZE] = std::mem::zeroed();
                buf
            };
            let mut written = 0;
            while next_len > 0 {
                let slice_len = next_len.min(DEFAULT_BUF_SIZE as i64) as usize;
                let len = match reader.read(&mut buf[..slice_len]) {
                    Ok(0) => {
                        // break early out of copy loop, because nothing is to be read anymore
                        srcpos += written;
                        break;
                    }
                    Ok(len) => len,
                    Err(ref err) if err.kind() == ErrorKind::Interrupted => continue,
                    Err(err) => return Err(err),
                };
                writer.write_all(&buf[..len])?;
                written += len as i64;
                next_len -= len as i64;
            }
            written as isize
        };
        srcpos += num as i64;
    }
    Ok(srcpos as u64)
}
pub fn copy_file_sparse ( from : & Path , to : & Path ) -> io :: Result < u64 > { use cvt :: * ; use fs :: OpenOptions ; use io :: { ErrorKind , Read } ; use libc :: { ftruncate64 , lseek64 } ; use std :: os :: unix :: { fs :: { OpenOptionsExt , PermissionsExt } , io :: AsRawFd , } ; unsafe fn copy_file_range ( fd_in : libc :: c_int , off_in : * mut libc :: loff_t , fd_out : libc :: c_int , off_out : * mut libc :: loff_t , len : libc :: size_t , flags : libc :: c_uint , ) -> libc :: c_long { libc :: syscall ( libc :: SYS_copy_file_range , fd_in , off_in , fd_out , off_out , len , flags , ) } let mut reader = File :: open ( from ) ? ; let ( mode , len ) = { let metadata = reader . metadata ( ) ? ; if ! metadata . is_file ( ) { return Err ( Error :: new ( ErrorKind :: InvalidInput , " " , ) ) ; } ( metadata . permissions ( ) . mode ( ) , metadata . len ( ) ) } ; let bytes_to_copy : i64 = len as i64 ; let mut writer = OpenOptions :: new ( ) // prevent world readable/writeable file in case of empty umask . mode ( 0o000 ) . write ( true ) . create ( true ) . truncate ( true ) . open ( to ) ? ; let fd_in = reader . as_raw_fd ( ) ; let fd_out = writer . as_raw_fd ( ) ; cvt_r ( | | unsafe { libc :: fchmod ( fd_out , mode ) } ) ? ; match cvt_r ( | | unsafe { ftruncate64 ( fd_out , bytes_to_copy ) } ) { Ok ( _ ) => { } Err ( err ) => return Err ( err ) , } ; let mut srcpos : i64 = 0 ; let ( mut can_handle_sparse , mut next_beg ) = { let ret = unsafe { lseek64 ( fd_in , srcpos , libc :: SEEK_DATA ) } ; if ret == - 1 { ( false , 0 ) } else { ( true , ret ) } } ; let mut next_end : libc :: loff_t = bytes_to_copy ; if can_handle_sparse { let ret = unsafe { lseek64 ( fd_in , next_beg , libc :: SEEK_HOLE ) } ; if ret == - 1 { can_handle_sparse = false ; } else { next_end = ret } } let mut next_len = next_end - next_beg ; let mut use_copy_file_range = true ; while srcpos < bytes_to_copy { if srcpos != 0 { if can_handle_sparse { next_beg = match cvt ( unsafe { lseek64 ( fd_in , srcpos , libc :: SEEK_DATA ) } ) { Ok ( beg ) => beg , Err ( err ) => match err . raw_os_error ( ) { Some ( libc :: ENXIO ) => { // Remaining portion is hole return Ok ( srcpos as u64 ) ; } _ => { return Err ( err ) ; } } , } ; next_end = cvt ( unsafe { lseek64 ( fd_in , next_beg , libc :: SEEK_HOLE ) } ) ? ; next_len = next_end - next_beg ; } else { next_beg = srcpos ; next_end = bytes_to_copy - srcpos ; } } if next_len <= 0 { srcpos = next_end ; continue ; } let num = if use_copy_file_range { match cvt ( unsafe { copy_file_range ( fd_in , & mut next_beg , fd_out , & mut next_beg , next_len as usize , 0 , ) } ) { Ok ( n ) => n as isize , Err ( err ) => match err . raw_os_error ( ) { // Try fallback if either: // - Kernel version is < 4.5 (ENOSYS) // - Files are mounted on different fs (EXDEV) // - copy_file_range is disallowed, for example by seccomp (EPERM) Some ( libc :: ENOSYS ) | Some ( libc :: EPERM ) => { use_copy_file_range = false ; continue ; } Some ( libc :: EXDEV ) | Some ( libc :: EINVAL ) => { use_copy_file_range = false ; continue ; } _ => { return Err ( err ) ; } } , } } else { if can_handle_sparse { cvt ( unsafe { lseek64 ( fd_in , next_beg , libc :: SEEK_SET ) } ) ? ; if next_beg != 0 { cvt ( unsafe { lseek64 ( fd_out , next_beg , libc :: SEEK_SET ) } ) ? ; } } const DEFAULT_BUF_SIZE : usize = 16 * 1024 ; let mut buf = unsafe { let buf : [ u8 ; DEFAULT_BUF_SIZE ] = std :: mem :: zeroed ( ) ; buf } ; let mut written = 0 ; while next_len > 0 { let slice_len = next_len . min ( DEFAULT_BUF_SIZE as i64 ) as usize ; let len = match reader . read ( & mut buf [ .. slice_len ] ) { Ok ( 0 ) => { // break early out of copy loop, because nothing is to be read anymore srcpos += written ; break ; } Ok ( len ) => len , Err ( ref err ) if err . kind ( ) == ErrorKind :: Interrupted => continue , Err ( err ) => return Err ( err ) , } ; writer . write_all ( & buf [ .. len ] ) ? ; written += len as i64 ; next_len -= len as i64 ; } written as isize } ; srcpos += num as i64 ; } Ok ( srcpos as u64 ) }
Copies only valid regions of file preserving the sparseness
of the file.
Copies only valid regions of file preserving the sparseness of the file .
from
&Path
to
&Path
from
&Path
to
&Path