diff --git a/Cargo.lock b/Cargo.lock index abdce585b..7cefee64c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -436,6 +436,17 @@ dependencies = [ "libc", ] +[[package]] +name = "idna" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + [[package]] name = "ipnetwork" version = "0.15.1" @@ -554,6 +565,12 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "matches" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" + [[package]] name = "memchr" version = "2.3.3" @@ -649,6 +666,12 @@ dependencies = [ "vm-memory", ] +[[package]] +name = "percent-encoding" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" + [[package]] name = "pin-utils" version = "0.1.0-alpha.4" @@ -1211,6 +1234,24 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "unicode-bidi" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" +dependencies = [ + "matches", +] + +[[package]] +name = "unicode-normalization" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5479532badd04e128284890390c1e876ef7a993d0570b3597ae43dfa1d59afa4" +dependencies = [ + "smallvec", +] + [[package]] name = "unicode-width" version = "0.1.7" @@ -1229,6 +1270,17 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" +[[package]] +name = "url" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "829d4a8476c35c9bf0bbce5a3b23f4106f79728039b726d292bb93bc106787cb" +dependencies = [ + "idna", + "matches", + "percent-encoding", +] + [[package]] name = "utf8-ranges" version = "1.0.4" @@ -1448,6 +1500,7 @@ dependencies = [ "serde_json", "signal-hook", "tempfile", + "url", "vfio", "vm-allocator", "vm-device", diff --git a/vmm/Cargo.toml b/vmm/Cargo.toml index b77d3e610..56c4e7117 100644 --- a/vmm/Cargo.toml +++ b/vmm/Cargo.toml @@ -32,6 +32,7 @@ seccomp = { git = "https://github.com/firecracker-microvm/firecracker", tag = "v serde = {version = ">=1.0.27", features = ["rc"] } serde_derive = ">=1.0.27" serde_json = ">=1.0.9" +url = "2.1.1" vfio = { path = "../vfio", optional = true } vm-allocator = { path = "../vm-allocator" } vm-device = { path = "../vm-device" } diff --git a/vmm/src/memory_manager.rs b/vmm/src/memory_manager.rs index ace1c798b..58a7fe6a0 100644 --- a/vmm/src/memory_manager.rs +++ b/vmm/src/memory_manager.rs @@ -17,10 +17,11 @@ use std::io; use std::os::unix::io::FromRawFd; use std::path::PathBuf; use std::sync::{Arc, Mutex}; +use url::Url; use vm_allocator::{GsiApic, SystemAllocator}; use vm_memory::guest_memory::FileOffset; use vm_memory::{ - mmap::MmapRegionError, Address, Error as MmapError, GuestAddress, GuestAddressSpace, + mmap::MmapRegionError, Address, Bytes, Error as MmapError, GuestAddress, GuestAddressSpace, GuestMemory, GuestMemoryAtomic, GuestMemoryLoadGuard, GuestMemoryMmap, GuestMemoryRegion, GuestRegionMmap, GuestUsize, MmapRegion, }; @@ -971,5 +972,67 @@ impl Snapshottable for MemoryManager { } } -impl Transportable for MemoryManager {} +impl Transportable for MemoryManager { + fn send( + &self, + _snapshot: &Snapshot, + destination_url: &str, + ) -> std::result::Result<(), MigratableError> { + let url = Url::parse(destination_url).map_err(|e| { + MigratableError::MigrateSend(anyhow!("Could not parse destination URL: {}", e)) + })?; + + match url.scheme() { + "file" => { + let vm_memory_snapshot_path = url + .to_file_path() + .map_err(|_| { + MigratableError::MigrateSend(anyhow!( + "Could not convert file URL to a file path" + )) + }) + .and_then(|path| { + if !path.is_dir() { + return Err(MigratableError::MigrateSend(anyhow!( + "Destination is not a directory" + ))); + } + Ok(path) + })?; + + if let Some(guest_memory) = &*self.snapshot.lock().unwrap() { + guest_memory.with_regions_mut(|index, region| { + let mut memory_region_path = vm_memory_snapshot_path.clone(); + memory_region_path.push(format!("memory-region-{}", index)); + + // Create the snapshot file for the region + let mut memory_region_file = OpenOptions::new() + .read(true) + .write(true) + .create_new(true) + .open(memory_region_path) + .map_err(|e| MigratableError::MigrateSend(e.into()))?; + + guest_memory + .write_to( + region.start_addr(), + &mut memory_region_file, + region.len().try_into().unwrap(), + ) + .map_err(|e| MigratableError::MigrateSend(e.into()))?; + + Ok(()) + })?; + } + } + _ => { + return Err(MigratableError::MigrateSend(anyhow!( + "Unsupported VM transport URL scheme: {}", + url.scheme() + ))) + } + } + Ok(()) + } +} impl Migratable for MemoryManager {}