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