diff --git a/offload_daemon/src/main.rs b/offload_daemon/src/main.rs index 5ca8ad2ce..66950db6a 100644 --- a/offload_daemon/src/main.rs +++ b/offload_daemon/src/main.rs @@ -17,7 +17,7 @@ use std::ffi::{CString, NulError}; use std::fs::{self, File, OpenOptions}; -use std::io::{self, Read, Seek, SeekFrom, Write}; +use std::io::{self, Read, Write}; use std::os::fd::{AsRawFd, FromRawFd}; use std::os::unix::net::{UnixListener, UnixStream}; use std::path::{Path, PathBuf}; @@ -30,6 +30,7 @@ use vm_migration::MigratableError; use vm_migration::protocol::{Command, Request, Response, Status}; use vmm::VmMigrationConfig; use vmm::migration::SNAPSHOT_STATE_FILE; +use vmm::sparse::copy_region; use vmm_sys_util::errno; use vmm_sys_util::sock_ctrl_msg::ScmSocket; @@ -308,32 +309,15 @@ fn dump_memory_slots( } fn dump_fd_to_path(file: &File, src_offset: u64, size: u64, path: &Path) -> Result<()> { - let mut out = OpenOptions::new() + let out = OpenOptions::new() .create(true) .truncate(true) .write(true) .open(path) .map_err(Error::WriteFile)?; out.set_len(size).map_err(Error::WriteFile)?; - // dup so our own offset doesn't disturb CH's view of the shared fd. - // SAFETY: dup() has no preconditions; result is checked. - let raw = unsafe { libc::dup(file.as_raw_fd()) }; - if raw < 0 { - return Err(Error::CopyMemory(io::Error::last_os_error())); - } - // SAFETY: `raw` is a fresh fd we now own. - let mut src = unsafe { File::from_raw_fd(raw) }; - src.seek(SeekFrom::Start(src_offset)) - .map_err(Error::CopyMemory)?; - let mut remaining = size; - let mut buf = vec![0u8; 1 << 20]; - while remaining > 0 { - let want = (remaining as usize).min(buf.len()); - src.read_exact(&mut buf[..want]) - .map_err(Error::CopyMemory)?; - out.write_all(&buf[..want]).map_err(Error::CopyMemory)?; - remaining -= want as u64; - } + // Keep `out` sparse: holes are left as holes (read back as zero). + copy_region(file, src_offset, &out, 0, size).map_err(Error::CopyMemory)?; out.sync_all().map_err(Error::WriteFile)?; Ok(()) } @@ -479,21 +463,10 @@ fn create_memfd_with_contents( name: &str, ) -> Result { // Size the memfd to cover the range CH maps at `file_offset`. - let mut memfd = create_empty_memfd(file_offset + size, name)?; - memfd - .seek(SeekFrom::Start(file_offset)) - .map_err(Error::CopyMemory)?; - let mut src = File::open(src_path).map_err(Error::ReadFile)?; - let mut remaining = size; - let mut buf = vec![0u8; 1 << 20]; - while remaining > 0 { - let want = (remaining as usize).min(buf.len()); - src.read_exact(&mut buf[..want]) - .map_err(Error::CopyMemory)?; - memfd.write_all(&buf[..want]).map_err(Error::CopyMemory)?; - remaining -= want as u64; - } - memfd.seek(SeekFrom::Start(0)).map_err(Error::CopyMemory)?; + let memfd = create_empty_memfd(file_offset + size, name)?; + let src = File::open(src_path).map_err(Error::ReadFile)?; + // Copy sparsely so the memfd keeps the snapshot's holes. + copy_region(&src, 0, &memfd, file_offset, size).map_err(Error::CopyMemory)?; Ok(memfd) }