1use std::io::{self, Read, Write};
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq)]
4#[repr(u8)]
5pub enum EntryKind {
6 Dir = 0,
7 File = 1,
8 Symlink = 2,
9}
10
11impl TryFrom<u8> for EntryKind {
12 type Error = io::Error;
13
14 fn try_from(v: u8) -> Result<Self, Self::Error> {
15 match v {
16 0 => Ok(EntryKind::Dir),
17 1 => Ok(EntryKind::File),
18 2 => Ok(EntryKind::Symlink),
19 _ => Err(io::Error::new(
20 io::ErrorKind::InvalidData,
21 format!("invalid entry kind: {v}"),
22 )),
23 }
24 }
25}
26
27#[derive(Debug, Clone, Copy, PartialEq, Eq)]
28#[repr(u8)]
29pub enum WorkingDir {
30 Inherit = 0,
31 PackageRoot = 1,
32 EntrypointParent = 2,
33}
34
35impl TryFrom<u8> for WorkingDir {
36 type Error = io::Error;
37
38 fn try_from(v: u8) -> Result<Self, Self::Error> {
39 match v {
40 0 => Ok(WorkingDir::Inherit),
41 1 => Ok(WorkingDir::PackageRoot),
42 2 => Ok(WorkingDir::EntrypointParent),
43 _ => Err(io::Error::new(
44 io::ErrorKind::InvalidData,
45 format!("invalid working_dir: {v}"),
46 )),
47 }
48 }
49}
50
51bitflags! {
52 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
53 pub struct EntryPointFlags: u8 {
54 const MEMFD_ELIGIBLE = 1 << 0;
55 }
56}
57
58#[derive(Debug, Clone)]
59pub struct Block {
60 pub payload_offset: u64,
62 pub compressed_size: u64,
64 pub original_size: u64,
66}
67
68pub const BLOCK_SIZE: usize = 24;
69
70impl Block {
71 pub fn write_to<W: Write>(&self, w: &mut W) -> io::Result<()> {
72 w.write_all(&self.payload_offset.to_le_bytes())?;
73 w.write_all(&self.compressed_size.to_le_bytes())?;
74 w.write_all(&self.original_size.to_le_bytes())?;
75 Ok(())
76 }
77
78 pub fn read_from<R: Read>(r: &mut R) -> io::Result<Self> {
79 let mut buf = [0u8; BLOCK_SIZE];
80 r.read_exact(&mut buf)?;
81
82 Ok(Block {
83 payload_offset: u64::from_le_bytes(buf[0..8].try_into().unwrap()),
84 compressed_size: u64::from_le_bytes(buf[8..16].try_into().unwrap()),
85 original_size: u64::from_le_bytes(buf[16..24].try_into().unwrap()),
86 })
87 }
88}
89
90#[derive(Debug, Clone)]
91pub struct EntryPoint {
92 pub name: u32,
94 pub target_entry: u32,
96 pub args: u32,
98 pub working_dir: WorkingDir,
100 pub flags: EntryPointFlags,
102}
103
104pub const ENTRYPOINT_SIZE: usize = 14;
106
107impl EntryPoint {
108 pub fn write_to<W: Write>(&self, w: &mut W) -> io::Result<()> {
109 w.write_all(&self.name.to_le_bytes())?;
110 w.write_all(&self.target_entry.to_le_bytes())?;
111 w.write_all(&self.args.to_le_bytes())?;
112 w.write_all(&[self.working_dir as u8])?;
113 w.write_all(&[self.flags.bits()])?;
114 Ok(())
115 }
116
117 pub fn read_from<R: Read>(r: &mut R) -> io::Result<Self> {
118 let mut buf = [0u8; ENTRYPOINT_SIZE];
119 r.read_exact(&mut buf)?;
120
121 Ok(EntryPoint {
122 name: u32::from_le_bytes(buf[0..4].try_into().unwrap()),
123 target_entry: u32::from_le_bytes(buf[4..8].try_into().unwrap()),
124 args: u32::from_le_bytes(buf[8..12].try_into().unwrap()),
125 working_dir: WorkingDir::try_from(buf[12])?,
126 flags: EntryPointFlags::from_bits_truncate(buf[13]),
127 })
128 }
129
130 pub fn is_memfd_eligible(&self) -> bool {
131 self.flags.contains(EntryPointFlags::MEMFD_ELIGIBLE)
132 }
133}
134
135#[derive(Debug, Clone)]
136pub struct Entry {
137 pub kind: EntryKind,
139 pub parent: u32,
141 pub name: u32,
143 pub mode: u32,
145 pub mtime_secs: u64,
147 pub mtime_nsec: u32,
149 pub content_hash: [u8; 32],
151 pub num_blocks: u32,
153 pub blocks: Vec<Block>,
155 pub symlink_target: u32,
157}
158
159pub const ENTRY_HEADER_SIZE: usize = 65;
163
164impl Entry {
165 pub fn write_to<W: Write>(&self, w: &mut W) -> io::Result<()> {
166 w.write_all(&[self.kind as u8])?;
167 w.write_all(&self.parent.to_le_bytes())?;
168 w.write_all(&self.name.to_le_bytes())?;
169 w.write_all(&self.mode.to_le_bytes())?;
170 w.write_all(&self.mtime_secs.to_le_bytes())?;
171 w.write_all(&self.mtime_nsec.to_le_bytes())?;
172 w.write_all(&self.content_hash)?;
173 w.write_all(&self.num_blocks.to_le_bytes())?;
174 w.write_all(&self.symlink_target.to_le_bytes())?;
175 for block in &self.blocks {
176 block.write_to(w)?;
177 }
178 Ok(())
179 }
180
181 pub fn read_from<R: Read>(r: &mut R) -> io::Result<Self> {
182 let mut buf = [0u8; ENTRY_HEADER_SIZE];
183 r.read_exact(&mut buf)?;
184
185 let kind = EntryKind::try_from(buf[0])?;
186 let parent = u32::from_le_bytes(buf[1..5].try_into().unwrap());
187 let name = u32::from_le_bytes(buf[5..9].try_into().unwrap());
188 let mode = u32::from_le_bytes(buf[9..13].try_into().unwrap());
189 let mtime_secs = u64::from_le_bytes(buf[13..21].try_into().unwrap());
190 let mtime_nsec = u32::from_le_bytes(buf[21..25].try_into().unwrap());
191 let mut content_hash = [0u8; 32];
192 content_hash.copy_from_slice(&buf[25..57]);
193 let num_blocks = u32::from_le_bytes(buf[57..61].try_into().unwrap());
194 let symlink_target = u32::from_le_bytes(buf[61..65].try_into().unwrap());
195
196 let mut blocks = Vec::with_capacity(num_blocks as usize);
197 for _ in 0..num_blocks {
198 blocks.push(Block::read_from(r)?);
199 }
200
201 Ok(Entry {
202 kind,
203 parent,
204 name,
205 mode,
206 mtime_secs,
207 mtime_nsec,
208 content_hash,
209 num_blocks,
210 blocks,
211 symlink_target,
212 })
213 }
214}