1 // Copyright 2024, The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 use core::cmp::{max, min};
16 use core::mem::size_of;
17 use fastboot::CommandError;
18
19 use static_assertions::const_assert;
20 use zerocopy::{AsBytes, FromBytes, FromZeroes, Ref};
21
22 // TODO(b/331854173): Switch to use bindgen for the following type definitions once
23 // system/core/libsparse is added to repo checkout.
24
25 const HEADER_MAGIC: u32 = 0xED26FF3A;
26 const CHUNK_TYPE_RAW: u16 = 0xCAC1;
27 const CHUNK_TYPE_FILL: u16 = 0xCAC2;
28 const CHUNK_TYPE_DONT_CARE: u16 = 0xCAC3;
29 const CHUNK_TYPE_CRC32: u16 = 0xCAC4;
30
31 const SPARSE_HEADER_MAJOR_VER: u16 = 1;
32 const SPARSE_HEADER_MINOR_VER: u16 = 0;
33
34 #[repr(C)]
35 #[derive(Debug, Default, Copy, Clone, AsBytes, FromBytes, FromZeroes)]
36 pub struct SparseHeader {
37 pub magic: u32,
38 pub major_version: u16,
39 pub minor_version: u16,
40 pub file_hdr_sz: u16,
41 pub chunk_hdr_sz: u16,
42 pub blk_sz: u32,
43 pub total_blks: u32,
44 pub total_chunks: u32,
45 pub image_checksum: u32,
46 }
47
48 #[repr(C)]
49 #[derive(Debug, Default, Copy, Clone, AsBytes, FromBytes, FromZeroes)]
50 pub struct ChunkHeader {
51 pub chunk_type: u16,
52 pub reserved1: u16,
53 pub chunk_sz: u32,
54 pub total_sz: u32,
55 }
56
57 const ERR_ARITHMETIC_OVERFLOW: &str = "Arithmetic Overflow";
58 const ERR_IMAGE_SIZE: &str = "Bad image. Invalid image size";
59
60 /// Checks if a sparse image is valid and returns the sparse header.
is_sparse_image(sparse_img: &[u8]) -> Result<SparseHeader, CommandError>61 pub fn is_sparse_image(sparse_img: &[u8]) -> Result<SparseHeader, CommandError> {
62 let sparse_header: SparseHeader = copy_from(sparse_img)?;
63 if sparse_header.magic != HEADER_MAGIC {
64 return Err("Sparse magic mismatch".into());
65 } else if sparse_header.major_version != SPARSE_HEADER_MAJOR_VER {
66 return Err("Sparse major version mismatch".into());
67 } else if sparse_header.minor_version != SPARSE_HEADER_MINOR_VER {
68 return Err("Sparse minor version mismatch".into());
69 }
70 Ok(sparse_header)
71 }
72
73 /// `FillInfo` is derived from a sparse chunk and contains information whether to fill a value or
74 /// skip for a number of blocks.
75 ///
76 /// Context and uses:
77 ///
78 /// When writing fill chunks from a sparse image, it is usually better to write a larger buffer
79 /// with the filled value instead of a single u32 at a time. However, separately maintaining a fill
80 /// buffer can be inconvenient for the caller. Therefore, we use a strategy that re-uses the input
81 /// buffer for fill buffer.
82 ///
83 /// The idea is to write the sparse image in two passes. In the first pass, we only write non-fill
84 /// chunks. For each sparse chunk, we create a `FillInfo` and append it from the beginning of the
85 /// input buffer. For fill chunks, `FillInfo::fill_blocks` and
86 /// `FillInfo::fill_value_or_skip_blocks` are set to the chunk size and fill value. For others,
87 /// `FillInfo::fill_blocks` will be set to 0 and `FillInfo::fill_value_or_skip_blocks` will be set
88 /// to the chunk size instead to represent number of blocks to skip. The second pass writes the
89 /// fill chunk according to `FillInfo`.
90 ///
91 /// Because a sparse chunk is at least 12 bytes, whereas `FillInfo` is 8 bytes, at the end of the
92 /// first pass, we are guaranteed to have at least 1/3 of the input buffer free to use as fill
93 /// buffer.
94 #[repr(C, packed)]
95 #[derive(Debug, Default, Copy, Clone, AsBytes, FromBytes, FromZeroes)]
96 struct FillInfo {
97 // Number of blocks to fill.
98 pub fill_blocks: u32,
99 // If `fill_blocks` is None, this field represents the number of blocks to skip.
100 // Otherwise, it represents the fill value.
101 pub fill_value_or_skip_blocks: u32,
102 }
103
104 impl FillInfo {
105 /// Creates an instance that represents filling a number of blocks.
new_fill(blocks: u32, value: u32) -> Self106 fn new_fill(blocks: u32, value: u32) -> Self {
107 assert_ne!(blocks, 0);
108 Self { fill_blocks: blocks, fill_value_or_skip_blocks: value }
109 }
110
111 /// Creates an instance that represents skipping a number of blocks.
new_skip(blocks: u32) -> Self112 fn new_skip(blocks: u32) -> Self {
113 Self { fill_blocks: 0, fill_value_or_skip_blocks: blocks }
114 }
115
116 // Returns (blocks, None) for the skip case or (blocks, Some(value)) for the fill case.
get_blocks_and_value(&self) -> (u32, Option<u32>)117 fn get_blocks_and_value(&self) -> (u32, Option<u32>) {
118 match self.fill_blocks {
119 0 => (self.fill_value_or_skip_blocks, None),
120 v => (v, Some(self.fill_value_or_skip_blocks)),
121 }
122 }
123 }
124
125 const_assert!(size_of::<FillInfo>() < size_of::<ChunkHeader>());
126
127 /// Write a sparse image in `sparse_img`.
128 ///
129 /// # Args
130 //
131 /// * `sparse_img`: The input buffer containing the sparse image. The API modifes input buffer for
132 /// internal optimization.
133 /// * `write`: A closure as the writer. It takes an offset and a `&mut [u8]` as the write data.
134 ///
135 /// # Returns
136 ///
137 /// Returns the total number of bytes written, including don't care chunks.
write_sparse_image<F>(sparse_img: &mut [u8], mut write: F) -> Result<u64, CommandError> where F: FnMut(u64, &mut [u8]) -> Result<(), CommandError>,138 pub fn write_sparse_image<F>(sparse_img: &mut [u8], mut write: F) -> Result<u64, CommandError>
139 where
140 F: FnMut(u64, &mut [u8]) -> Result<(), CommandError>,
141 {
142 let sparse_header: SparseHeader = is_sparse_image(sparse_img)?;
143 let mut curr: usize = size_of::<SparseHeader>();
144 let mut write_offset = 0u64;
145
146 // First pass. Writes non-fill chunk and constructs `FillInfo`.
147 let mut fill_off = 0usize;
148 for _ in 0..sparse_header.total_chunks {
149 let header: ChunkHeader = copy_from(&mut sparse_img[curr..])?;
150 let payload = &mut sparse_img[curr + size_of::<ChunkHeader>()..];
151 let payload_sz = u64_mul(header.chunk_sz, sparse_header.blk_sz)?;
152 let mut fill = FillInfo::new_skip(header.chunk_sz);
153 match header.chunk_type {
154 CHUNK_TYPE_RAW => write(write_offset, get_mut(payload, 0, to_usize(payload_sz)?)?)?,
155 CHUNK_TYPE_FILL if header.chunk_sz != 0 => {
156 let fill_val = u32::from_le_bytes(get_mut(payload, 0, 4)?.try_into().unwrap());
157 fill = FillInfo::new_fill(header.chunk_sz, fill_val);
158 }
159 CHUNK_TYPE_DONT_CARE | CHUNK_TYPE_CRC32 => {}
160 _ => return Err("Invalid Chunk Type".into()),
161 };
162 write_offset = u64_add(write_offset, payload_sz)?;
163 sparse_img[fill_off..][..size_of::<FillInfo>()].clone_from_slice(fill.as_bytes());
164 fill_off = usize_add(fill_off, size_of::<FillInfo>())?;
165 curr = usize_add(curr, header.total_sz)?;
166 }
167 let total = write_offset;
168
169 // Second pass. Writes fill chunks.
170 // Use all reamining buffer as fill buffer.
171 let (fill_infos, fill_buffer) = sparse_img.split_at_mut(fill_off);
172 let mut fill_buffer = FillBuffer { curr_val: None, curr_size: 0, buffer: fill_buffer };
173 let fill_infos = Ref::<_, [FillInfo]>::new_slice(fill_infos).unwrap().into_slice();
174 write_offset = 0;
175 for ele in fill_infos {
176 match ele.get_blocks_and_value() {
177 (blks, None) => {
178 write_offset = u64_add(write_offset, u64_mul(blks, sparse_header.blk_sz)?)?;
179 }
180 (blks, Some(v)) => {
181 let sz = u64_mul(blks, sparse_header.blk_sz)?;
182 let buffer = fill_buffer.get(v, sz)?;
183 let buffer_len = to_u64(buffer.len())?;
184 let end = u64_add(write_offset, sz)?;
185 while write_offset < end {
186 let to_write = min(buffer_len, end - write_offset);
187 write(write_offset, &mut buffer[..to_usize(to_write).unwrap()])?;
188 write_offset += to_write;
189 }
190 }
191 }
192 }
193 Ok(total)
194 }
195
196 /// `FillUnit` is a packed C struct wrapping a u32. It is mainly used for filling a buffer of
197 /// arbitrary alignment with a u32 value.
198 #[repr(C, packed)]
199 #[derive(Debug, Default, Copy, Clone, AsBytes, FromBytes, FromZeroes)]
200 struct FillUnit(u32);
201
202 /// `FillBuffer` manages a buffer and provides API for making a fill buffer with the given value.
203 struct FillBuffer<'a> {
204 curr_val: Option<u32>,
205 curr_size: usize,
206 buffer: &'a mut [u8],
207 }
208
209 impl FillBuffer<'_> {
210 /// Get a buffer up to `size` number of bytes filled with `val`.
get(&mut self, val: u32, size: u64) -> Result<&mut [u8], CommandError>211 fn get(&mut self, val: u32, size: u64) -> Result<&mut [u8], CommandError> {
212 let aligned_len = self.buffer.len() - (self.buffer.len() % size_of::<u32>());
213 let size: usize = min(to_u64(aligned_len)?, size).try_into().unwrap();
214 if Some(val) != self.curr_val {
215 self.curr_size = 0;
216 self.curr_val = Some(val);
217 }
218 let gap = max(self.curr_size, size) - self.curr_size;
219 let to_fill = &mut self.buffer[self.curr_size..][..gap];
220 Ref::<_, [FillUnit]>::new_slice(to_fill).unwrap().into_mut_slice().fill(FillUnit(val));
221 self.curr_size += gap;
222 Ok(&mut self.buffer[..size])
223 }
224 }
225
226 /// A helper to check and get a mutable sub slice.
get_mut<L: TryInto<usize>, R: TryInto<usize>>( bytes: &mut [u8], start: L, end: R, ) -> Result<&mut [u8], CommandError>227 fn get_mut<L: TryInto<usize>, R: TryInto<usize>>(
228 bytes: &mut [u8],
229 start: L,
230 end: R,
231 ) -> Result<&mut [u8], CommandError> {
232 bytes.get_mut(to_usize(start)?..to_usize(end)?).ok_or(ERR_IMAGE_SIZE.into())
233 }
234
235 /// A helper to check and get a sub slice.
get<L: TryInto<usize>, R: TryInto<usize>>( bytes: &[u8], start: L, end: R, ) -> Result<&[u8], CommandError>236 fn get<L: TryInto<usize>, R: TryInto<usize>>(
237 bytes: &[u8],
238 start: L,
239 end: R,
240 ) -> Result<&[u8], CommandError> {
241 bytes.get(to_usize(start)?..to_usize(end)?).ok_or(ERR_IMAGE_SIZE.into())
242 }
243
244 /// A helper to return a copy of a zerocopy object from bytes.
copy_from<T: AsBytes + FromBytes + Default>(bytes: &[u8]) -> Result<T, CommandError>245 fn copy_from<T: AsBytes + FromBytes + Default>(bytes: &[u8]) -> Result<T, CommandError> {
246 let mut res: T = Default::default();
247 res.as_bytes_mut().clone_from_slice(get(bytes, 0, size_of::<T>())?);
248 Ok(res)
249 }
250
251 /// Checks and converts an integer into usize.
to_usize<T: TryInto<usize>>(val: T) -> Result<usize, CommandError>252 fn to_usize<T: TryInto<usize>>(val: T) -> Result<usize, CommandError> {
253 Ok(val.try_into().map_err(|_| ERR_ARITHMETIC_OVERFLOW)?)
254 }
255
256 /// Adds two usize convertible numbers and checks overflow.
usize_add<L: TryInto<usize>, R: TryInto<usize>>(lhs: L, rhs: R) -> Result<usize, CommandError>257 fn usize_add<L: TryInto<usize>, R: TryInto<usize>>(lhs: L, rhs: R) -> Result<usize, CommandError> {
258 Ok(to_usize(lhs)?.checked_add(to_usize(rhs)?).ok_or(ERR_ARITHMETIC_OVERFLOW)?)
259 }
260
261 /// Checks and converts an integer into u64
to_u64<T: TryInto<u64>>(val: T) -> Result<u64, CommandError>262 fn to_u64<T: TryInto<u64>>(val: T) -> Result<u64, CommandError> {
263 Ok(val.try_into().map_err(|_| ERR_ARITHMETIC_OVERFLOW)?)
264 }
265
266 /// Adds two u64 convertible numbers and checks overflow.
u64_add<L: TryInto<u64>, R: TryInto<u64>>(lhs: L, rhs: R) -> Result<u64, CommandError>267 fn u64_add<L: TryInto<u64>, R: TryInto<u64>>(lhs: L, rhs: R) -> Result<u64, CommandError> {
268 Ok(to_u64(lhs)?.checked_add(to_u64(rhs)?).ok_or(ERR_ARITHMETIC_OVERFLOW)?)
269 }
270
271 /// Multiplies two u64 convertible numbers and checks overflow.
u64_mul<L: TryInto<u64>, R: TryInto<u64>>(lhs: L, rhs: R) -> Result<u64, CommandError>272 fn u64_mul<L: TryInto<u64>, R: TryInto<u64>>(lhs: L, rhs: R) -> Result<u64, CommandError> {
273 Ok(to_u64(lhs)?.checked_mul(to_u64(rhs)?).ok_or(ERR_ARITHMETIC_OVERFLOW)?)
274 }
275
276 #[cfg(test)]
277 mod test {
278 use super::*;
279
280 #[test]
test_sparse_write()281 fn test_sparse_write() {
282 let raw = include_bytes!("../../testdata/sparse_test_raw.bin");
283 let sparse = include_bytes!("../../testdata/sparse_test.bin");
284 // Gives a larger output buffer.
285 let mut out = vec![0u8; 2 * raw.len()];
286 assert_eq!(
287 write_sparse_image(&mut sparse.to_vec()[..], |off, data| {
288 out[off.try_into().unwrap()..][..data.len()].clone_from_slice(data);
289 Ok(())
290 })
291 .unwrap(),
292 raw.len().try_into().unwrap()
293 );
294 assert_eq!(out[..raw.len()].to_vec(), raw);
295 }
296
297 #[test]
test_sparse_write_non_default_block_size()298 fn test_sparse_write_non_default_block_size() {
299 let raw = include_bytes!("../../testdata/sparse_test_raw.bin");
300 let sparse = include_bytes!("../../testdata/sparse_test_blk1024.bin");
301 // Gives a larger output buffer.
302 let mut out = vec![0u8; 2 * raw.len()];
303 assert_eq!(
304 write_sparse_image(&mut sparse.to_vec()[..], |off, data| {
305 out[off.try_into().unwrap()..][..data.len()].clone_from_slice(data);
306 Ok(())
307 })
308 .unwrap(),
309 raw.len().try_into().unwrap()
310 );
311 assert_eq!(out[..raw.len()].to_vec(), raw);
312 }
313
314 /// A helper to copy a zerocopy object into a buffer
copy_to<T: AsBytes + FromBytes>(val: &T, bytes: &mut [u8])315 fn copy_to<T: AsBytes + FromBytes>(val: &T, bytes: &mut [u8]) {
316 bytes[..size_of::<T>()].clone_from_slice(val.as_bytes());
317 }
318
319 #[test]
test_sparse_invalid_magic()320 fn test_sparse_invalid_magic() {
321 let mut sparse = include_bytes!("../../testdata/sparse_test.bin").to_vec();
322 let mut sparse_header: SparseHeader = copy_from(&sparse[..]).unwrap();
323 sparse_header.magic = 0;
324 copy_to(&sparse_header, &mut sparse[..]);
325 assert!(write_sparse_image(&mut sparse[..], |_, _| panic!()).is_err());
326 }
327
328 #[test]
test_sparse_invalid_major_version()329 fn test_sparse_invalid_major_version() {
330 let mut sparse = include_bytes!("../../testdata/sparse_test.bin").to_vec();
331 let mut sparse_header: SparseHeader = copy_from(&sparse[..]).unwrap();
332 sparse_header.major_version = SPARSE_HEADER_MAJOR_VER + 1;
333 copy_to(&sparse_header, &mut sparse[..]);
334 assert!(write_sparse_image(&mut sparse[..], |_, _| panic!()).is_err());
335 }
336
337 #[test]
test_sparse_invalid_minor_version()338 fn test_sparse_invalid_minor_version() {
339 let mut sparse = include_bytes!("../../testdata/sparse_test.bin").to_vec();
340 let mut sparse_header: SparseHeader = copy_from(&sparse[..]).unwrap();
341 sparse_header.minor_version = SPARSE_HEADER_MINOR_VER + 1;
342 copy_to(&sparse_header, &mut sparse[..]);
343 assert!(write_sparse_image(&mut sparse[..], |_, _| panic!()).is_err());
344 }
345 }
346