1pub mod generated;
2#[cfg(not(target_arch = "wasm32"))]
3pub mod pack;
4pub mod versioned;
5
6use std::ops::Range;
7
8use crate::posix::vfs::{VfsError, VfsResult};
9
10pub const AOSPKG_MAGIC: [u8; 4] = [0x89, b'A', b'O', b'S'];
11pub const AOSPKG_FORMAT_VERSION: u16 = 1;
12pub const AOSPKG_HEADER_LEN: usize = 16;
13
14#[derive(Debug, Clone, PartialEq, Eq)]
15pub struct AospkgHeader {
16 pub manifest_len: u32,
17 pub index_len: u32,
18 pub manifest: Range<usize>,
19 pub index: Range<usize>,
20 pub mount: Range<usize>,
21}
22
23pub fn parse_aospkg_header(bytes: &[u8]) -> VfsResult<AospkgHeader> {
31 parse_aospkg_header_from_prefix(bytes, bytes.len())
32}
33
34pub fn parse_aospkg_header_from_prefix(header: &[u8], file_len: usize) -> VfsResult<AospkgHeader> {
35 if header.len() < AOSPKG_HEADER_LEN {
36 return Err(VfsError::new(
37 "EINVAL",
38 format!(
39 ".aospkg header truncated: {} bytes < {AOSPKG_HEADER_LEN} bytes",
40 header.len()
41 ),
42 ));
43 }
44
45 if file_len < AOSPKG_HEADER_LEN {
46 return Err(VfsError::new(
47 "EINVAL",
48 format!(".aospkg file truncated: {file_len} bytes < {AOSPKG_HEADER_LEN} bytes"),
49 ));
50 }
51
52 if header[0..4] != AOSPKG_MAGIC {
53 return Err(VfsError::new("EINVAL", "invalid .aospkg magic"));
54 }
55 let format_version = u16::from_le_bytes([header[4], header[5]]);
56 if format_version != AOSPKG_FORMAT_VERSION {
57 return Err(VfsError::new(
58 "EINVAL",
59 format!("unsupported .aospkg format version: {format_version}"),
60 ));
61 }
62 let flags = u16::from_le_bytes([header[6], header[7]]);
63 if flags != 0 {
64 return Err(VfsError::new(
65 "EINVAL",
66 format!("unsupported .aospkg flags: {flags}"),
67 ));
68 }
69
70 let manifest_len = u32::from_le_bytes([header[8], header[9], header[10], header[11]]);
71 let index_len = u32::from_le_bytes([header[12], header[13], header[14], header[15]]);
72 let manifest_len_usize = usize::try_from(manifest_len).map_err(|_| {
73 VfsError::new(
74 "EOVERFLOW",
75 format!(".aospkg manifest length overflows usize: {manifest_len} bytes"),
76 )
77 })?;
78 let index_len_usize = usize::try_from(index_len).map_err(|_| {
79 VfsError::new(
80 "EOVERFLOW",
81 format!(".aospkg index length overflows usize: {index_len} bytes"),
82 )
83 })?;
84 let index_start = AOSPKG_HEADER_LEN
85 .checked_add(manifest_len_usize)
86 .ok_or_else(|| VfsError::new("EOVERFLOW", ".aospkg manifest range overflows usize"))?;
87 let mount_start = index_start
88 .checked_add(index_len_usize)
89 .ok_or_else(|| VfsError::new("EOVERFLOW", ".aospkg index range overflows usize"))?;
90 if mount_start > file_len {
91 return Err(VfsError::new(
92 "EINVAL",
93 format!(
94 ".aospkg chunks exceed file size: header {AOSPKG_HEADER_LEN} bytes + manifest {manifest_len} bytes + index {index_len} bytes > {file_len} bytes"
95 ),
96 ));
97 }
98
99 Ok(AospkgHeader {
100 manifest_len,
101 index_len,
102 manifest: AOSPKG_HEADER_LEN..index_start,
103 index: index_start..mount_start,
104 mount: mount_start..file_len,
105 })
106}
107
108pub fn encode_aospkg_header(manifest_len: usize, index_len: usize) -> VfsResult<[u8; 16]> {
109 let manifest_len = u32::try_from(manifest_len).map_err(|_| {
110 VfsError::new(
111 "EOVERFLOW",
112 format!(".aospkg manifest chunk too large: {manifest_len} bytes > u32::MAX bytes"),
113 )
114 })?;
115 let index_len = u32::try_from(index_len).map_err(|_| {
116 VfsError::new(
117 "EOVERFLOW",
118 format!(".aospkg index chunk too large: {index_len} bytes > u32::MAX bytes"),
119 )
120 })?;
121 let mut header = [0u8; AOSPKG_HEADER_LEN];
122 header[0..4].copy_from_slice(&AOSPKG_MAGIC);
123 header[4..6].copy_from_slice(&AOSPKG_FORMAT_VERSION.to_le_bytes());
124 header[6..8].copy_from_slice(&0u16.to_le_bytes());
125 header[8..12].copy_from_slice(&manifest_len.to_le_bytes());
126 header[12..16].copy_from_slice(&index_len.to_le_bytes());
127 Ok(header)
128}
129
130#[cfg(not(target_arch = "wasm32"))]
136pub fn read_manifest_chunk_from_file(
137 path: &std::path::Path,
138) -> VfsResult<generated::v1::PackageManifest> {
139 use std::io::{Read, Seek, SeekFrom};
140 let mut file = std::fs::File::open(path)
141 .map_err(|e| VfsError::new("EIO", format!("open {}: {e}", path.display())))?;
142 let file_len = file
143 .metadata()
144 .map_err(|e| VfsError::new("EIO", format!("stat {}: {e}", path.display())))?
145 .len();
146 let file_len = usize::try_from(file_len).map_err(|_| {
147 VfsError::new(
148 "EOVERFLOW",
149 format!(
150 "{} is too large to address on this platform",
151 path.display()
152 ),
153 )
154 })?;
155 let mut header = [0u8; AOSPKG_HEADER_LEN];
156 file.read_exact(&mut header)
157 .map_err(|e| VfsError::new("EIO", format!("read {} header: {e}", path.display())))?;
158 let parsed = parse_aospkg_header_from_prefix(&header, file_len)?;
159 let mut manifest = vec![0u8; parsed.manifest.len()];
160 file.seek(SeekFrom::Start(parsed.manifest.start as u64))
161 .map_err(|e| VfsError::new("EIO", format!("seek {} manifest: {e}", path.display())))?;
162 file.read_exact(&mut manifest)
163 .map_err(|e| VfsError::new("EIO", format!("read {} manifest: {e}", path.display())))?;
164 versioned::decode_package_manifest(&manifest).map_err(|error| {
165 VfsError::new(
166 "EINVAL",
167 format!("decode package manifest in {}: {error}", path.display()),
168 )
169 })
170}
171
172pub fn validate_mount_range(
173 header: &AospkgHeader,
174 offset: u64,
175 size: u64,
176) -> VfsResult<Range<usize>> {
177 let mount_base = u64::try_from(header.mount.start).map_err(|_| {
178 VfsError::new(
179 "EOVERFLOW",
180 format!(
181 ".aospkg mount base overflows u64: {} bytes",
182 header.mount.start
183 ),
184 )
185 })?;
186 let absolute_start = mount_base
187 .checked_add(offset)
188 .ok_or_else(|| VfsError::new("EOVERFLOW", ".aospkg member start overflows u64"))?;
189 let absolute_end = absolute_start
190 .checked_add(size)
191 .ok_or_else(|| VfsError::new("EOVERFLOW", ".aospkg member end overflows u64"))?;
192 let file_len = u64::try_from(header.mount.end).map_err(|_| {
193 VfsError::new(
194 "EOVERFLOW",
195 format!(
196 ".aospkg file length overflows u64: {} bytes",
197 header.mount.end
198 ),
199 )
200 })?;
201 if absolute_end > file_len {
202 return Err(VfsError::new(
203 "EIO",
204 format!(
205 ".aospkg member range exceeds file size: mountBase {mount_base} bytes + offset {offset} bytes + size {size} bytes > {file_len} bytes"
206 ),
207 ));
208 }
209 let start = usize::try_from(absolute_start).map_err(|_| {
210 VfsError::new(
211 "EOVERFLOW",
212 format!(".aospkg member start overflows usize: {absolute_start} bytes"),
213 )
214 })?;
215 let end = usize::try_from(absolute_end).map_err(|_| {
216 VfsError::new(
217 "EOVERFLOW",
218 format!(".aospkg member end overflows usize: {absolute_end} bytes"),
219 )
220 })?;
221 Ok(start..end)
222}