Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Temporarily vendor imago #253

Merged
merged 1 commit into from
Jan 20, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 1 addition & 5 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src/devices/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ utils = { path = "../utils" }
polly = { path = "../polly" }
rutabaga_gfx = { path = "../rutabaga_gfx", features = ["virgl_renderer", "virgl_renderer_next"], optional = true }

imago = { version = "0.1.2", features = ["sync-wrappers", "vm-memory"] }
imago = { path = "../imago", features = ["sync-wrappers", "vm-memory"] }

[target.'cfg(target_os = "macos")'.dependencies]
hvf = { path = "../hvf" }
Expand Down
6 changes: 6 additions & 0 deletions src/imago/.cargo_vcs_info.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"git": {
"sha1": "6d4fbca7dd85c4d740261c91f0350d3403cc6ee5"
},
"path_in_vcs": ""
}
1 change: 1 addition & 0 deletions src/imago/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/target
71 changes: 71 additions & 0 deletions src/imago/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
#
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
# to registry (e.g., crates.io) dependencies.
#
# If you are reading this file be aware that the original Cargo.toml
# will likely look very different (and much more reasonable).
# See Cargo.toml.orig for the original contents.

[package]
edition = "2021"
name = "imago"
version = "0.1.3"
build = "build.rs"
autolib = false
autobins = false
autoexamples = false
autotests = false
autobenches = false
description = "A library for accessing virtual machine disk images."
readme = "README.md"
license = "MIT"
repository = "https://gitlab.com/hreitz/imago"

[package.metadata.docs.rs]
all-features = true

[features]
default = []
sync-wrappers = []
vm-memory = ["dep:vm-memory"]

[lib]
name = "imago"
path = "src/lib.rs"

[dependencies.async-trait]
version = "0.1"

[dependencies.bincode]
version = "1.3"

[dependencies.miniz_oxide]
version = "0.8"
features = ["std"]

[dependencies.serde]
version = "1.0"
features = ["derive"]

[dependencies.tokio]
version = "1"
features = [
"rt",
"sync",
]

[dependencies.tracing]
version = "0.1"

[dependencies.vm-memory]
version = "0.16"
optional = true

[build-dependencies.rustc_version]
version = "0.4.0"

[target."cfg(unix)".dependencies.libc]
version = "0.2"
19 changes: 19 additions & 0 deletions src/imago/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
Copyright (c) 2024 imago contributors

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
76 changes: 76 additions & 0 deletions src/imago/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# Imago

Provides access to VM image formats.

Simple example (requires the `sync-wrappers` feature):
```rust
use imago::file::File;
use imago::qcow2::Qcow2;
use imago::SyncFormatAccess;
use std::fs::OpenOptions;

// Produce read-only qcow2 instance using purely `File` for storage
let mut qcow2 = Qcow2::<File>::open_path_sync("image.qcow2", false)?;
qcow2.open_implicit_dependencies_sync()?;

let qcow2 = SyncFormatAccess::new(qcow2)?;

let mut buf = vec![0u8; 512];
qcow2.read(&mut buf, 0)?;
```

Another example, using the native async interface instead of sync wrapper functions, explicitly
overriding the implicit references contained in qcow2 files, and showcasing using different
types of storage (specifically normal files and null storage):
```rust
use imago::file::File;
use imago::null::Null;
use imago::qcow2::Qcow2;
use imago::raw::Raw;
use imago::{DynStorage, FormatAccess, Storage, StorageOpenOptions};
use std::sync::Arc;

let qcow2_file_opts = StorageOpenOptions::new()
.write(true)
.filename(String::from("image.qcow2"));
let qcow2_file = File::open(qcow2_file_opts).await?;

// Produce qcow2 instance with arbitrary (and potentially mixed) storage instances
let mut qcow2 =
Qcow2::<Box<dyn DynStorage>, Arc<FormatAccess<_>>>::open_image(Box::new(qcow2_file), true)
.await?;

let backing_storage: Box<dyn DynStorage> = Box::new(Null::new(0));
let backing = Raw::open_image(backing_storage, false).await?;
let backing = Arc::new(FormatAccess::new(backing));
qcow2.set_backing(Some(Arc::clone(&backing)));

// Open potentially remaining dependencies (like an external data file)
qcow2.open_implicit_dependencies().await?;

let qcow2 = FormatAccess::new(qcow2);

let mut buf = vec![0u8; 512];
qcow2.read(&mut buf, 0).await?;

qcow2.flush().await?;
```

# Flushing

Given that `AsyncDrop` is not stable yet (and probably will not be stable for a long time),
callers must ensure that images are properly flushed before dropping them, i.e. call
`.flush().await` on any image that is not read-only.

(The synchronous wrapper `SyncFormatAccess` does perform a synchronous flush in its `Drop`
implementation.)

# Features

- `sync-wrappers`: Provide synchronous wrappers for the native `async` interface. Note that
these build a `tokio` runtime in which they run the `async` functions, so using the `async`
interface is definitely preferred.

- `vm-memory`: Provide conversion functions `IoVector::from_volatile_slice` and
`IoVectorMut::from_volatile_slice` to convert the vm-memory crate’s `[VolatileSlice]` arrays into
imago’s native I/O vectors.
9 changes: 9 additions & 0 deletions src/imago/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
use rustc_version::{version_meta, Channel};

fn main() {
println!("cargo:rustc-check-cfg=cfg(nightly)");

if version_meta().unwrap().channel == Channel::Nightly {
println!("cargo:rustc-cfg=nightly");
}
}
3 changes: 3 additions & 0 deletions src/imago/rustfmt.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
edition = "2021"
format_code_in_doc_comments = true
imports_granularity = "Module"
172 changes: 172 additions & 0 deletions src/imago/src/annotated.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
//! Annotating wrapper around storage objects.
//!
//! Wraps other storage objects, adding an arbitrary tag to them.
//!
//! This may be useful when using the “mapping” interface, to identify the storage objects returned
//! in raw mappings.
//!
//! Example:
//! ```
//! # use imago::{FormatAccess, Mapping};
//! # use imago::annotated::Annotated;
//! # use imago::null::Null;
//! # use imago::raw::Raw;
//! # tokio::runtime::Builder::new_current_thread()
//! # .build()
//! # .unwrap()
//! # .block_on(async move {
//! #
//! const TEST_TAG: u32 = 42;
//!
//! let disk_size = 16 << 30;
//! let test_offset = 1 << 30;
//!
//! let inner_storage = Null::new(disk_size);
//! let annotated_storage = Annotated::new(inner_storage, TEST_TAG);
//! let image = Raw::open_image(annotated_storage, false).await?;
//! let image = FormatAccess::new(image);
//!
//! let mapping = image.get_mapping(test_offset, 1).await?.0;
//! let Mapping::Raw {
//! storage,
//! offset,
//! writable,
//! } = mapping
//! else {
//! panic!("Raw mapping expected");
//! };
//! assert_eq!(*storage.tag(), TEST_TAG);
//! assert_eq!(offset, test_offset);
//! #
//! # Ok::<(), std::io::Error>(())
//! # }).unwrap()
//! ```

use crate::io_buffers::{IoVector, IoVectorMut};
use crate::storage::drivers::CommonStorageHelper;
use crate::{Storage, StorageOpenOptions};
use std::fmt::{self, Debug, Display, Formatter};
use std::io;
use std::ops::{Deref, DerefMut};
use std::path::{Path, PathBuf};

/// Annotating wrapper around storage objects.
///
/// Wraps other storage objects, adding an arbitrary tag to them.
// TODO: Remove the `Default` requirement. We want to implement `Storage::open()` if `Default` is
// implemented, though, but return an error if it is not. Doing that probably requires
// specialization, though.
#[derive(Debug)]
pub struct Annotated<Tag: Debug + Default + Display + Send + Sync, S: Storage> {
/// Wrapped storage object.
inner: S,

/// Tag.
tag: Tag,
}

impl<T: Debug + Default + Display + Send + Sync, S: Storage> Annotated<T, S> {
/// Wrap `storage`, adding the tag `tag`.
pub fn new(storage: S, tag: T) -> Self {
Annotated {
inner: storage,
tag,
}
}

/// Get the tag.
pub fn tag(&self) -> &T {
&self.tag
}

/// Allow modifying or changing the tag.
pub fn tag_mut(&mut self) -> &mut T {
&mut self.tag
}
}

impl<T: Debug + Default + Display + Send + Sync, S: Storage> From<S> for Annotated<T, S> {
fn from(storage: S) -> Self {
Self::new(storage, T::default())
}
}

impl<T: Debug + Default + Display + Send + Sync, S: Storage> Storage for Annotated<T, S> {
async fn open(opts: StorageOpenOptions) -> io::Result<Self> {
Ok(S::open(opts).await?.into())
}

#[cfg(feature = "sync-wrappers")]
fn open_sync(opts: StorageOpenOptions) -> io::Result<Self> {
Ok(S::open_sync(opts)?.into())
}

fn mem_align(&self) -> usize {
self.inner.mem_align()
}

fn req_align(&self) -> usize {
self.inner.req_align()
}

fn size(&self) -> io::Result<u64> {
self.inner.size()
}

fn resolve_relative_path<P: AsRef<Path>>(&self, relative: P) -> io::Result<PathBuf> {
self.inner.resolve_relative_path(relative)
}

async unsafe fn pure_readv(&self, bufv: IoVectorMut<'_>, offset: u64) -> io::Result<()> {
// Caller guarantees safety
unsafe { self.inner.pure_readv(bufv, offset) }.await
}

async unsafe fn pure_writev(&self, bufv: IoVector<'_>, offset: u64) -> io::Result<()> {
// Caller guarantees safety
unsafe { self.inner.pure_writev(bufv, offset) }.await
}

async unsafe fn pure_write_zeroes(&self, offset: u64, length: u64) -> io::Result<()> {
// Caller guarantees safety
unsafe { self.inner.pure_write_zeroes(offset, length) }.await
}

async unsafe fn pure_discard(&self, offset: u64, length: u64) -> io::Result<()> {
// Caller guarantees safety
unsafe { self.inner.pure_discard(offset, length) }.await
}

async fn flush(&self) -> io::Result<()> {
self.inner.flush().await
}

async fn sync(&self) -> io::Result<()> {
self.inner.sync().await
}

fn get_storage_helper(&self) -> &CommonStorageHelper {
// Share storage helper from inner (to e.g. get same request serialization)
self.inner.get_storage_helper()
}
}

impl<T: Debug + Default + Display + Send + Sync, S: Storage> Deref for Annotated<T, S> {
type Target = S;

fn deref(&self) -> &S {
&self.inner
}
}

impl<T: Debug + Default + Display + Send + Sync, S: Storage> DerefMut for Annotated<T, S> {
fn deref_mut(&mut self) -> &mut S {
&mut self.inner
}
}

impl<T: Debug + Default + Display + Send + Sync, S: Storage> Display for Annotated<T, S> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "annotated({})[{}]", self.tag, self.inner)
}
}
Loading
Loading