Skip to content

Commit bfaf058

Browse files
committed
feat(hermit-image-reader): Add tar bytes wrappers
1 parent 2543c2e commit bfaf058

File tree

5 files changed

+73
-35
lines changed

5 files changed

+73
-35
lines changed

hermit-image-reader/examples/hermit-image-info.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ fn main() {
44
let data = std::fs::read(f).expect("unable to read image file");
55
let decompressed = hermit_image_reader::decompress_image(&data[..])
66
.expect("unable to decompress image file");
7-
for i in hermit_image_reader::ImageParser::new(&decompressed[..]) {
7+
for i in hermit_image_reader::ImageParser::new(&decompressed) {
88
let i = i.expect("unable to read image entry");
99
print!(" Entry ");
1010
let maybe_name = i.name.try_as_str();

hermit-image-reader/src/lib.rs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,19 @@
22

33
extern crate alloc;
44

5-
use alloc::vec::Vec;
6-
75
/// We assume that all images are gzip-compressed
8-
pub fn decompress_image(data: &[u8]) -> Result<Vec<u8>, compression::prelude::CompressionError> {
6+
pub fn decompress_image(data: &[u8]) -> Result<TarBytes, compression::prelude::CompressionError> {
97
use compression::prelude::{DecodeExt as _, GZipDecoder};
108

119
data.iter()
1210
.copied()
1311
.decode(&mut GZipDecoder::new())
14-
.collect()
12+
.collect::<Result<_, _>>()
13+
.map(TarBytes)
1514
}
1615

1716
mod tar_parser;
18-
pub use tar_parser::{ImageFile, ImageParser, ImageParserError};
17+
pub use tar_parser::{ImageFile, ImageParser, ImageParserError, TarBytes, TarBytesSlice};
1918

2019
pub mod config;
2120

hermit-image-reader/src/tar_parser.rs

Lines changed: 52 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
1-
use alloc::vec::Vec;
2-
use core::ops::Range;
1+
use alloc::{boxed::Box, vec::Vec};
2+
use core::ops::{Deref, Range};
33

44
use crate::filename::{Filename, truncate};
55

66
/// A parser for an already decompressed image
77
#[derive(Clone, Copy)]
88
pub struct ImageParser<'a> {
9-
input: &'a [u8],
9+
input: &'a TarBytesSlice,
1010
offset: usize,
1111
}
1212

1313
impl<'a> ImageParser<'a> {
14-
pub fn new(input: &'a [u8]) -> Self {
14+
pub fn new(input: &'a TarBytesSlice) -> Self {
1515
Self { input, offset: 0 }
1616
}
1717
}
@@ -56,6 +56,35 @@ impl<'a> From<core::str::Utf8Error> for ImageParserError<'a> {
5656
}
5757
}
5858

59+
/// Owned bytes belonging to a decompressed tar file
60+
#[derive(Clone, Default, PartialEq, Eq)]
61+
pub struct TarBytes(pub Box<[u8]>);
62+
63+
#[derive(PartialEq, Eq)]
64+
#[repr(transparent)]
65+
pub struct TarBytesSlice(pub [u8]);
66+
67+
impl Deref for TarBytes {
68+
type Target = TarBytesSlice;
69+
70+
#[inline(always)]
71+
fn deref(&self) -> &TarBytesSlice {
72+
TarBytesSlice::new(&self.0[..])
73+
}
74+
}
75+
76+
impl TarBytesSlice {
77+
#[inline(always)]
78+
pub const fn new(data: &[u8]) -> &Self {
79+
unsafe { &*(data as *const [u8] as *const Self) }
80+
}
81+
82+
pub const fn split_at(&self, mid: usize) -> (&Self, &Self) {
83+
let (a, b) = self.0.split_at(mid);
84+
(Self::new(a), Self::new(b))
85+
}
86+
}
87+
5988
fn try_parse_octal<'a, T: num_traits::Num>(s: &[u8]) -> Result<T, ImageParserError<'a>>
6089
where
6190
T::FromStrRadixErr: Into<ImageParserError<'a>>,
@@ -68,38 +97,38 @@ const BLOCK_SIZE_2POW: u32 = 9;
6897

6998
impl<'a> ImageParser<'a> {
7099
fn next_intern(&mut self) -> Result<Option<ImageFile<'a>>, ImageParserError<'a>> {
71-
while self.input.len() >= BLOCK_SIZE {
100+
while self.input.0.len() >= BLOCK_SIZE {
72101
// `input` starts with a tar header, padded to 512 bytes (block size)
73102
let offset = self.offset;
74103
let (header, rest) = self.input.split_at(BLOCK_SIZE);
75104

76105
// note that integers are usually encoded as octal numbers
77-
let name = truncate(&header[0..100]);
78-
if header.iter().take_while(|i| **i == 0).count() == BLOCK_SIZE {
106+
let name = truncate(&header.0[0..100]);
107+
if header.0.iter().take_while(|i| **i == 0).count() == BLOCK_SIZE {
79108
// EOF marker
80109
return Ok(None);
81110
}
82-
let is_exec = if let Ok(mode) = try_parse_octal::<u16>(&header[100..108]) {
111+
let is_exec = if let Ok(mode) = try_parse_octal::<u16>(&header.0[100..108]) {
83112
mode & 0o111 != 0
84113
} else {
85114
false
86115
};
87-
let size: usize = try_parse_octal::<u64>(&header[124..136])?.try_into()?;
88-
let _linkname = &header[157..257];
89-
let magic = &header[257..263];
90-
let _version = &header[263..265];
91-
let prefix = &header[345..500];
116+
let size: usize = try_parse_octal::<u64>(&header.0[124..136])?.try_into()?;
117+
let _linkname = &header.0[157..257];
118+
let magic = &header.0[257..263];
119+
let _version = &header.0[263..265];
120+
let prefix = &header.0[345..500];
92121

93122
// check if this is a supported file type
94-
let ret = match header[156] {
123+
let ret = match header.0[156] {
95124
0 | b'0' => {
96125
// regular file
97126
let value_offset = offset + BLOCK_SIZE;
98127
Some(ImageFile {
99128
name: Filename::One(name),
100129
is_exec,
101130
value_range: value_offset..(value_offset + size),
102-
value: rest.get(..size).ok_or(ImageParserError::UnexpectedEof)?,
131+
value: rest.0.get(..size).ok_or(ImageParserError::UnexpectedEof)?,
103132
})
104133
}
105134
_ => None,
@@ -117,9 +146,11 @@ impl<'a> ImageParser<'a> {
117146
tmp << BLOCK_SIZE_2POW
118147
};
119148
self.offset += actual_rest_size;
120-
self.input = rest
121-
.get(actual_rest_size..)
122-
.ok_or(ImageParserError::UnexpectedEof)?;
149+
self.input = TarBytesSlice::new(
150+
rest.0
151+
.get(actual_rest_size..)
152+
.ok_or(ImageParserError::UnexpectedEof)?,
153+
);
123154

124155
if let Some(mut x) = ret {
125156
// gather full file name (we might have to honor the ustar prefix)
@@ -133,7 +164,7 @@ impl<'a> ImageParser<'a> {
133164
}
134165
}
135166

136-
if self.input.is_empty() {
167+
if self.input.0.is_empty() {
137168
return Ok(None);
138169
}
139170
Err(ImageParserError::UnexpectedEof)
@@ -149,7 +180,7 @@ impl<'a> Iterator for ImageParser<'a> {
149180
Ok(Some(x)) => Some(Ok(x)),
150181
Err(e) => {
151182
// make sure we don't get stuck
152-
self.input = &[];
183+
self.input = TarBytesSlice::new(&[]);
153184
Some(Err(e))
154185
}
155186
}
@@ -165,7 +196,7 @@ mod tests {
165196
proptest! {
166197
#[test]
167198
fn doesnt_crash(data: Vec<u8>) {
168-
ImageParser::new(&*data).count();
199+
ImageParser::new(TarBytesSlice::new(&*data)).count();
169200
}
170201
}
171202
}

hermit-image-reader/src/thin_tree.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use alloc::collections::btree_map::BTreeMap;
22

3-
use crate::{ImageParser, ImageParserError, StrFilename};
3+
use crate::{ImageParser, ImageParserError, StrFilename, TarBytesSlice};
44

55
#[derive(Clone, Debug, PartialEq, Eq, yoke::Yokeable)]
66
pub enum ThinTreeRef<'a> {
@@ -39,7 +39,7 @@ impl<'a> ThinTreeRef<'a> {
3939
Ok(())
4040
}
4141

42-
pub fn try_from_image(image: &'a [u8]) -> Result<Self, ImageParserError<'a>> {
42+
pub fn try_from_image(image: &'a TarBytesSlice) -> Result<Self, ImageParserError<'a>> {
4343
let mut content = Self::File(b"");
4444
for i in ImageParser::new(image) {
4545
let i = i?;

src/isolation/image.rs

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use std::{
22
collections::HashMap,
3+
fmt,
34
fs::canonicalize,
45
path::{Path, PathBuf},
56
sync::Arc,
@@ -9,14 +10,23 @@ pub use hermit_image_reader::thin_tree::ThinTreeRef as HermitImageThinTree;
910
use yoke::Yoke;
1011

1112
/// A "mounted" hermit image, the decompressed contents of it are mmap'ed into this process
12-
pub type HermitImage = Box<[u8]>;
13+
pub type HermitImage = hermit_image_reader::TarBytes;
1314

14-
#[derive(Clone, Debug)]
15+
#[derive(Clone)]
1516
pub enum MappedFile {
1617
OnHost(PathBuf),
1718
InImage(Yoke<HermitImageThinTree<'static>, Arc<HermitImage>>),
1819
}
1920

21+
impl fmt::Debug for MappedFile {
22+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
23+
match self {
24+
MappedFile::OnHost(oh) => write!(f, "OnHost({:?})", oh),
25+
MappedFile::InImage(_) => write!(f, "InImage(..)"),
26+
}
27+
}
28+
}
29+
2030
impl MappedFile {
2131
#[cfg(test)]
2232
pub(super) fn unwrap_on_host(self) -> PathBuf {
@@ -26,9 +36,7 @@ impl MappedFile {
2636
panic!("unexpected mapped file: {:?}", self)
2737
}
2838
}
29-
}
3039

31-
impl MappedFile {
3240
pub fn resolve(&self, entry: &Path) -> Option<Self> {
3341
match self {
3442
MappedFile::OnHost(host_path) => {
@@ -83,10 +91,10 @@ impl Cache {
8391
)
8492
});
8593

86-
let image: Arc<Box<[u8]>> = Arc::new(decompressed.into());
94+
let image: Arc<HermitImage> = Arc::new(decompressed);
8795

8896
Yoke::attach_to_cart(image, |image| {
89-
HermitImageThinTree::try_from_image(&image[..]).unwrap_or_else(|e| {
97+
HermitImageThinTree::try_from_image(image).unwrap_or_else(|e| {
9098
panic!(
9199
"{}: unable to parse hermit image file entry: {:?}",
92100
host_path.display(),

0 commit comments

Comments
 (0)