From da5422ef8cdb53a62d28661c951c54fcb0145add Mon Sep 17 00:00:00 2001 From: Allison Karlitskaya Date: Sat, 5 Oct 2024 17:50:31 +0200 Subject: [PATCH] all: use anyhow I was getting sick of typing std::io::Result<()> all the time. Also: some guy on users.rust-lang.org says that it's cool, even in libraries. This is a very literal port with very few features actually used. --- Cargo.toml | 1 + src/bin/mount.rs | 21 ++++++++++++--------- src/bin/splitstream.rs | 4 +++- src/fsverity/ioctl.rs | 11 +++++++---- src/repository.rs | 27 ++++++++++++++------------- src/splitstream.rs | 13 ++++++++----- src/tar.rs | 16 ++++++++++------ src/tmpdir.rs | 13 +++++++++---- 8 files changed, 64 insertions(+), 42 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d947e8f..5f6f7b5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,6 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] +anyhow = { version = "1.0.89", features = ["backtrace"] } clap = { version = "4.5.19", features = ["derive"] } hex = "0.4.3" rand = "0.8.5" diff --git a/src/bin/mount.rs b/src/bin/mount.rs index d1b47de..40e42d1 100644 --- a/src/bin/mount.rs +++ b/src/bin/mount.rs @@ -1,3 +1,11 @@ +use std::os::fd::{ + OwnedFd, + BorrowedFd, + AsFd, + AsRawFd +}; + +use anyhow::Result; use rustix::mount::{ FsMountFlags, FsOpenFlags, @@ -11,12 +19,7 @@ use rustix::mount::{ move_mount, unmount, }; -use std::os::fd::{ - OwnedFd, - BorrowedFd, - AsFd, - AsRawFd -}; + use composefs_experiments::{ fsverity, tmpdir, @@ -27,7 +30,7 @@ struct FsHandle { } impl FsHandle { - pub fn open(name: &str) -> std::io::Result { + pub fn open(name: &str) -> Result { Ok(FsHandle { fd: fsopen(name, FsOpenFlags::FSOPEN_CLOEXEC)? }) } } @@ -56,7 +59,7 @@ struct TmpMount { } impl TmpMount { - pub fn mount(fs: BorrowedFd) -> std::io::Result { + pub fn mount(fs: BorrowedFd) -> Result { let tmp = tmpdir::TempDir::new()?; let mnt = fsmount(fs, FsMountFlags::FSMOUNT_CLOEXEC, MountAttrFlags::empty())?; move_mount(mnt.as_fd(), "", rustix::fs::CWD, &tmp.path, MoveMountFlags::MOVE_MOUNT_F_EMPTY_PATH)?; @@ -95,7 +98,7 @@ impl<'a> MountOptions<'a> { self.digest = Some(digest); } - fn mount(self, mountpoint: &str) -> std::io::Result<()> { + fn mount(self, mountpoint: &str) -> Result<()> { let image = std::fs::File::open(self.image)?; if let Some(expected) = self.digest { diff --git a/src/bin/splitstream.rs b/src/bin/splitstream.rs index f9e8bd9..ddd9122 100644 --- a/src/bin/splitstream.rs +++ b/src/bin/splitstream.rs @@ -1,3 +1,5 @@ +use anyhow::Result; + use composefs_experiments::{ fsverity::Sha256HashValue, tar::split, @@ -11,7 +13,7 @@ fn main() { split( &mut std::io::stdin(), &mut std::io::stdout(), - |data: &[u8]| -> std::io::Result { + |data: &[u8]| -> Result { repo.ensure_object(&data) } ).expect("split"); diff --git a/src/fsverity/ioctl.rs b/src/fsverity/ioctl.rs index aadac68..ecd1287 100644 --- a/src/fsverity/ioctl.rs +++ b/src/fsverity/ioctl.rs @@ -1,7 +1,10 @@ -use super::FsVerityHashValue; use std::os::fd::AsFd; + +use anyhow::Result; use rustix::ioctl; +use super::FsVerityHashValue; + // See /usr/include/linux/fsverity.h #[repr(C)] pub struct FsVerityEnableArg { @@ -19,7 +22,7 @@ pub struct FsVerityEnableArg { // #define FS_IOC_ENABLE_VERITY _IOW('f', 133, struct fsverity_enable_arg) type FsIocEnableVerity = ioctl::WriteOpcode; -pub fn fs_ioc_enable_verity(fd: F) -> std::io::Result<()> { +pub fn fs_ioc_enable_verity(fd: F) -> Result<()> { unsafe { ioctl::ioctl(fd, ioctl::Setter::::new(FsVerityEnableArg { version: 1, @@ -47,7 +50,7 @@ pub struct FsVerityDigest { // #define FS_IOC_MEASURE_VERITY _IORW('f', 134, struct fsverity_digest) type FsIocMeasureVerity = ioctl::ReadWriteOpcode>; -pub fn fs_ioc_measure_verity(fd: F) -> std::io::Result { +pub fn fs_ioc_measure_verity(fd: F) -> Result { let digest_size = std::mem::size_of::() as u16; let digest_algorithm = H::ALGORITHM as u16; @@ -58,7 +61,7 @@ pub fn fs_ioc_measure_verity(fd: F) -> std::io::R } if digest.digest_algorithm != digest_algorithm || digest.digest_size != digest_size { - Err(std::io::Error::from(std::io::ErrorKind::InvalidData)) + Err(std::io::Error::from(std::io::ErrorKind::InvalidData))? } else { Ok(digest.digest) } diff --git a/src/repository.rs b/src/repository.rs index 699e359..1a44ce9 100644 --- a/src/repository.rs +++ b/src/repository.rs @@ -9,6 +9,7 @@ use std::path::{ PathBuf, }; +use anyhow::Result; use rustix::fs::{ Access, AtFlags, @@ -51,22 +52,22 @@ impl Drop for Repository { } impl Repository { - pub fn open_fd(repository: OwnedFd) -> std::io::Result { + pub fn open_fd(repository: OwnedFd) -> Result { flock(&repository, FlockOperation::LockShared)?; Ok(Repository { repository }) } - pub fn open_path(path: P) -> std::io::Result { + pub fn open_path(path: P) -> Result { // O_PATH isn't enough because flock() Repository::open_fd(open(path, OFlags::RDONLY, Mode::empty())?) } - pub fn open_default() -> std::io::Result { + pub fn open_default() -> Result { let home = PathBuf::from(std::env::var("HOME").expect("$HOME must be set")); Repository::open_path(home.join(".var/lib/composefs")) } - fn ensure_parent>(&self, path: P) -> std::io::Result<()> { + fn ensure_parent>(&self, path: P) -> Result<()> { match path.as_ref().parent() { None => Ok(()), Some(path) if path == Path::new("") => Ok(()), @@ -74,7 +75,7 @@ impl Repository { } } - fn ensure_dir>(&self, dir: P) -> std::io::Result<()> { + fn ensure_dir>(&self, dir: P) -> Result<()> { self.ensure_parent(&dir)?; match mkdirat(&self.repository, dir.as_ref(), 0o777.into()) { @@ -83,7 +84,7 @@ impl Repository { Err(err) => Err(err.into()) } } - pub fn ensure_object(&self, data: &[u8]) -> std::io::Result { + pub fn ensure_object(&self, data: &[u8]) -> Result { let digest = FsVerityHasher::hash(data); let dir = PathBuf::from(format!("objects/{:02x}", digest[0])); let file = dir.join(hex::encode(&digest[1..])); @@ -118,17 +119,17 @@ impl Repository { Ok(digest) } - pub fn merge_splitstream(&self, name: &str, stream: &mut W) -> std::io::Result<()> { + pub fn merge_splitstream(&self, name: &str, stream: &mut W) -> Result<()> { Ok(()) } - pub fn import_tar(&self, name: &str, tar_stream: &mut R) -> std::io::Result<()> { + pub fn import_tar(&self, name: &str, tar_stream: &mut R) -> Result<()> { let mut split_stream = zstd::stream::write::Encoder::new(vec![], 0)?; tar::split( tar_stream, &mut split_stream, - |data: &[u8]| -> std::io::Result { + |data: &[u8]| -> Result { self.ensure_object(data) })?; @@ -138,7 +139,7 @@ impl Repository { fn link_ref( &self, name: &str, category: &str, object_id: Sha256HashValue - ) -> std::io::Result<()> { + ) -> Result<()> { let object_path = format!("objects/{:02x}/{}", object_id[0], hex::encode(&object_id[1..])); let category_path = format!("{}/{}", category, hex::encode(&object_id)); let ref_path = format!("refs/{}", name); @@ -148,7 +149,7 @@ impl Repository { Ok(()) } - fn symlink>(&self, name: P, target: &str) -> std::io::Result<()> { + fn symlink>(&self, name: P, target: &str) -> Result<()> { let name = name.as_ref(); let parent = name.parent() .expect("make_link() called for file directly in repo top-level"); @@ -163,7 +164,7 @@ impl Repository { Ok(symlinkat(target_path, &self.repository, name)?) } - pub fn gc(&self) -> std::io::Result<()> { + pub fn gc(&self) -> Result<()> { flock(&self.repository, FlockOperation::LockExclusive)?; // TODO: GC @@ -171,7 +172,7 @@ impl Repository { Ok(flock(&self.repository, FlockOperation::LockShared)?) // XXX: finally { } ? } - pub fn fsck(&self) -> std::io::Result<()> { + pub fn fsck(&self) -> Result<()> { Ok(()) } } diff --git a/src/splitstream.rs b/src/splitstream.rs index 67a874b..432346f 100644 --- a/src/splitstream.rs +++ b/src/splitstream.rs @@ -28,6 +28,9 @@ */ use std::io::Write; + +use anyhow::Result; + use crate::fsverity::Sha256HashValue; // utility class to help write splitstreams @@ -42,13 +45,13 @@ impl<'w, W: Write> SplitStreamWriter<'w, W> { SplitStreamWriter { inline_content: vec![], writer } } - fn write_fragment(writer: &mut W, size: usize, data: &[u8]) -> std::io::Result<()> { + fn write_fragment(writer: &mut W, size: usize, data: &[u8]) -> Result<()> { writer.write_all(&(size as u64).to_le_bytes())?; - writer.write_all(data) + Ok(writer.write_all(data)?) } /// flush any buffered inline data, taking new_value as the new value of the buffer - fn flush_inline(&mut self, new_value: Vec) -> std::io::Result<()> { + fn flush_inline(&mut self, new_value: Vec) -> Result<()> { if !self.inline_content.is_empty() { SplitStreamWriter::write_fragment(self.writer, self.inline_content.len(), &self.inline_content)?; self.inline_content = new_value; @@ -65,7 +68,7 @@ impl<'w, W: Write> SplitStreamWriter<'w, W> { /// write a reference to external data to the stream. If the external data had padding in the /// stream which is not stored in the object then pass it here as well and it will be stored /// inline after the reference. - pub fn write_reference(&mut self, reference: Sha256HashValue, padding: Vec) -> std::io::Result<()> { + pub fn write_reference(&mut self, reference: Sha256HashValue, padding: Vec) -> Result<()> { // Flush the inline data before we store the external reference. Any padding from the // external data becomes the start of a new inline block. self.flush_inline(padding)?; @@ -73,7 +76,7 @@ impl<'w, W: Write> SplitStreamWriter<'w, W> { SplitStreamWriter::write_fragment(self.writer, 0, &reference) } - pub fn done(&mut self) -> std::io::Result<()> { + pub fn done(&mut self) -> Result<()> { self.flush_inline(vec![]) } } diff --git a/src/tar.rs b/src/tar.rs index 101f99c..404522b 100644 --- a/src/tar.rs +++ b/src/tar.rs @@ -1,4 +1,7 @@ use std::io::{Read, Write}; + +use anyhow::Result; + use crate::fsverity::Sha256HashValue; use crate::splitstream::SplitStreamWriter; @@ -8,23 +11,24 @@ struct TarHeader { impl TarHeader { // we can't use Read::read_exact() because we need to be able to detect EOF - fn read(reader: &mut R) -> std::io::Result> { + fn read(reader: &mut R) -> Result> { let mut header = TarHeader { data: [0u8; 512] }; let mut todo: &mut [u8] = &mut header.data; while !todo.is_empty() { match reader.read(todo) { Ok(0) => match todo.len() { - 512 => return Ok(None), - _ => return Err(std::io::ErrorKind::UnexpectedEof.into()), + 512 => return Ok(None), // clean EOF + _ => Err(std::io::Error::from(std::io::ErrorKind::UnexpectedEof))? }, Ok(n) => { todo = &mut todo[n..]; } Err(e) if e.kind() == std::io::ErrorKind::Interrupted => { + continue; } Err(e) => { - return Err(e); + Err(e)?; } } } @@ -72,11 +76,11 @@ impl TarHeader { /// Splits the tar file from tar_stream into a Split Stream. The store_data function is /// responsible for ensuring that "external data" is in the composefs repository and returns the /// fsverity hash value of that data. -pub fn split std::io::Result>( +pub fn split Result>( tar_stream: &mut R, split_stream: &mut W, mut store_data: F, -) -> std::io::Result<()> { +) -> Result<()> { let mut writer = SplitStreamWriter::new(split_stream); while let Some(header) = TarHeader::read(tar_stream)? { diff --git a/src/tmpdir.rs b/src/tmpdir.rs index fb29ea8..ff384d1 100644 --- a/src/tmpdir.rs +++ b/src/tmpdir.rs @@ -1,12 +1,17 @@ -use rand::distributions::{Alphanumeric, DistString}; use std::path::PathBuf; +use anyhow::{ + Result, + bail, +}; +use rand::distributions::{Alphanumeric, DistString}; + pub struct TempDir { pub path: PathBuf, } impl TempDir { - pub fn new() -> std::io::Result{ + pub fn new() -> Result{ let tmp = PathBuf::from("/tmp"); for _ in 0 .. 26*26*26 { // this is how many times glibc tries @@ -15,11 +20,11 @@ impl TempDir { match std::fs::create_dir(&path) { Ok(()) => return Ok(TempDir { path }), Err(e) if e.kind() == std::io::ErrorKind::AlreadyExists => continue, - Err(e) => return Err(e) + Err(e) => Err(e)? } } - Err(std::io::Error::from(std::io::ErrorKind::AlreadyExists)) + bail!("Failed to find free name for temporary directory"); } }