Skip to content

Commit

Permalink
rust: Add a composefs-sys crate
Browse files Browse the repository at this point in the history
We can expand this in the future; for now I started by just binding
the digest API, which we definitely need when doing things in
Rust in the future.

Signed-off-by: Colin Walters <[email protected]>
  • Loading branch information
cgwalters committed May 28, 2024
1 parent 750a239 commit 1eaf11d
Show file tree
Hide file tree
Showing 7 changed files with 151 additions and 2 deletions.
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[workspace]
members = ["rust/composefs-core"]
members = ["rust/composefs-core", "rust/composefs-sys"]
resolver = "2"

[profile.dev]
Expand All @@ -15,4 +15,4 @@ debug = true
[profile.releaselto]
codegen-units = 1
inherits = "release"
lto = "yes"
lto = "yes"
1 change: 1 addition & 0 deletions rust/composefs-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ path = "src/lib.rs"
[dependencies]
anyhow = "1.0"
libc = "0.2"
composefs-sys = { path = "../composefs-sys" }

[dev-dependencies]
tar = "0.4.38"
Expand Down
61 changes: 61 additions & 0 deletions rust/composefs-core/src/fsverity.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
//! # Bindings for computing fsverity
//!
//! This collection of APIs is for computing fsverity digests as
//! used by composefs.

use std::os::fd::{AsRawFd, BorrowedFd};

use composefs_sys::{map_result, LCFS_SHA256_DIGEST_LEN};

/// The binary composefs digest
#[derive(Debug, PartialEq, Eq, Clone, Default)]
pub struct Digest([u8; LCFS_SHA256_DIGEST_LEN]);

impl Digest {
/// Create an uninitialized digest.
pub fn new() -> Self {
Self::default()
}

/// Retrieve the digest bytes
pub fn get(&self) -> &[u8; LCFS_SHA256_DIGEST_LEN] {
&self.0
}
}

/// Compute the composefs fsverity digest from the provided file descriptor.
#[allow(unsafe_code)]
pub fn fsverity_digest_from_fd(fd: BorrowedFd, digest: &mut Digest) -> std::io::Result<()> {
unsafe {
map_result(composefs_sys::lcfs_compute_fsverity_from_fd(
digest.0.as_mut_ptr(),
fd.as_raw_fd(),
))
}
}

#[cfg(test)]
mod tests {
use anyhow::Result;
use std::io::{Seek, Write};
use std::os::fd::AsFd;

use super::*;

#[test]
fn test_digest() -> Result<()> {
let mut tf = tempfile::tempfile()?;
tf.write_all(b"hello world")?;
let mut digest = Digest::new();
tf.seek(std::io::SeekFrom::Start(0))?;
fsverity_digest_from_fd(tf.as_fd(), &mut digest)?;
assert_eq!(
digest.get(),
&[
30, 46, 170, 66, 2, 215, 80, 164, 17, 116, 238, 69, 73, 112, 185, 44, 27, 194, 249,
37, 177, 227, 80, 118, 216, 199, 213, 245, 99, 98, 186, 100
]
);
Ok(())
}
}
2 changes: 2 additions & 0 deletions rust/composefs-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ use dumpfile::Entry;
pub mod dumpfile;
pub mod mkcomposefs;

pub mod fsverity;

/// Parse a composefs superblock.
pub fn dump<F>(f: File, mut callback: F) -> Result<()>
where
Expand Down
19 changes: 19 additions & 0 deletions rust/composefs-sys/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
[package]
name = "composefs-sys"
version = "0.1.0"
edition = "2021"
links = "composefs"
build = "build.rs"

[package.metadata.system-deps.composefs]
name = "composefs"
version = "1"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[build-dependencies]
system-deps = "6"

[dev-dependencies]
anyhow = "1"
tempfile = "3"
6 changes: 6 additions & 0 deletions rust/composefs-sys/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
fn main() {
if let Err(s) = system_deps::Config::new().probe() {
println!("cargo:warning={s}");
std::process::exit(1);
}
}
60 changes: 60 additions & 0 deletions rust/composefs-sys/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
//! # Bindings for libcomposefs
//!
//! This crate contains a few manually maintained system bindings for libcomposefs.
pub const LCFS_SHA256_DIGEST_LEN: usize = 32;

extern "C" {
pub fn lcfs_compute_fsverity_from_fd(
digest: *mut u8,
fd: std::os::raw::c_int,
) -> std::os::raw::c_int;
pub fn lcfs_fd_enable_fsverity(fd: std::os::raw::c_int) -> std::os::raw::c_int;
}

/// Convert an integer return value into a `Result`.
pub fn map_result(r: std::os::raw::c_int) -> std::io::Result<()> {
match r {
0 => Ok(()),
_ => Err(std::io::Error::last_os_error()),
}
}

#[cfg(test)]
mod tests {
use anyhow::Result;
use std::io::{Seek, Write};
use std::os::fd::AsRawFd;

use super::*;

#[test]
fn test_fd_enable_fsverity() -> Result<()> {
// We can't require fsverity in our test suite, so just verify we can call the
// function.
let mut tf = tempfile::NamedTempFile::new()?;
tf.write_all(b"hello")?;
let tf = std::fs::File::open(tf.path())?;
let _ = unsafe { lcfs_fd_enable_fsverity(tf.as_raw_fd()) };
Ok(())
}

#[test]
fn test_digest() -> Result<()> {
unsafe {
let mut tf = tempfile::tempfile()?;
tf.write_all(b"hello world")?;
let mut buf = [0u8; LCFS_SHA256_DIGEST_LEN];
tf.seek(std::io::SeekFrom::Start(0))?;
let r = lcfs_compute_fsverity_from_fd(buf.as_mut_ptr(), tf.as_raw_fd());
assert_eq!(r, 0);
assert_eq!(
buf,
[
30, 46, 170, 66, 2, 215, 80, 164, 17, 116, 238, 69, 73, 112, 185, 44, 27, 194,
249, 37, 177, 227, 80, 118, 216, 199, 213, 245, 99, 98, 186, 100
]
);
Ok(())
}
}
}

0 comments on commit 1eaf11d

Please sign in to comment.