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