• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 //! This file implements storage and partition logic for libgbl.
16 
17 use crate::fastboot::sparse::{is_sparse_image, write_sparse_image, SparseRawWriter};
18 use core::cell::{RefCell, RefMut};
19 use core::{
20     ffi::CStr,
21     ops::{Deref, DerefMut},
22 };
23 use gbl_storage::{
24     BlockInfo, BlockIo, Disk, Gpt, GptBuilder, GptSyncResult, Partition as GptPartition,
25     SliceMaybeUninit,
26 };
27 use liberror::Error;
28 use safemath::SafeNum;
29 
30 /// Maximum name length for raw partition.
31 pub const RAW_PARTITION_NAME_LEN: usize = 72;
32 
33 /// Wraps a bytes buffer containing a null-terminated C string
34 #[derive(Copy, Clone, PartialEq, Debug)]
35 pub struct RawName([u8; RAW_PARTITION_NAME_LEN]);
36 
37 impl RawName {
new(name: &CStr) -> Result<Self, Error>38     fn new(name: &CStr) -> Result<Self, Error> {
39         let mut buf = [0u8; RAW_PARTITION_NAME_LEN];
40         name.to_str().map_err(|_| Error::InvalidInput)?;
41         let name = name.to_bytes_with_nul();
42         buf.get_mut(..name.len()).ok_or(Error::InvalidInput)?.clone_from_slice(name);
43         Ok(Self(buf))
44     }
45 
46     /// Decodes to a string.
to_str(&self) -> &str47     pub fn to_str(&self) -> &str {
48         CStr::from_bytes_until_nul(&self.0[..]).unwrap().to_str().unwrap()
49     }
50 }
51 
52 /// Represents a GBL partition.
53 #[derive(Copy, Clone, PartialEq, Debug)]
54 pub enum Partition {
55     /// Raw storage partition.
56     Raw(RawName, u64),
57     /// Gpt Partition.
58     Gpt(GptPartition),
59 }
60 
61 impl Partition {
62     /// Returns the size.
size(&self) -> Result<u64, Error>63     pub fn size(&self) -> Result<u64, Error> {
64         let (start, end) = self.absolute_range()?;
65         Ok((SafeNum::from(end) - start).try_into()?)
66     }
67 
68     /// Returns the name.
name(&self) -> Result<&str, Error>69     pub fn name(&self) -> Result<&str, Error> {
70         Ok(match self {
71             Partition::Gpt(gpt) => gpt.name().ok_or(Error::InvalidInput)?,
72             Partition::Raw(name, _) => name.to_str(),
73         })
74     }
75 
76     /// Computes the absolute start and end offset for the partition in the whole block device.
absolute_range(&self) -> Result<(u64, u64), Error>77     pub fn absolute_range(&self) -> Result<(u64, u64), Error> {
78         Ok(match self {
79             Partition::Gpt(gpt) => gpt.absolute_range()?,
80             Partition::Raw(_, size) => (0, *size),
81         })
82     }
83 }
84 
85 /// Represents the partition table for a block device. It can either be a GPT partition table or a
86 /// single whole device raw partition.
87 enum PartitionTable<G> {
88     Raw(RawName, u64),
89     Gpt(G),
90 }
91 
92 /// The status of block device
93 pub enum BlockStatus {
94     /// Idle,
95     Idle,
96     /// An IO in progress.
97     Pending,
98 }
99 
100 impl BlockStatus {
101     /// Converts to str.
to_str(&self) -> &'static str102     pub fn to_str(&self) -> &'static str {
103         match self {
104             BlockStatus::Idle => "idle",
105             BlockStatus::Pending => "IO pending",
106         }
107     }
108 }
109 
110 /// Represents a disk device that contains either GPT partitions or a single whole raw storage
111 /// partition.
112 pub struct GblDisk<D, G> {
113     // Contains a `Disk` for block IO.
114     //
115     // `disk` and `partitions` are wrapped in RefCell because they may be shared by multiple async
116     // blocks for operations such as parallel fastboot download/flashing. They are also wrapped
117     // separately in order to make operations on each independent and parallel for use cases such
118     // as getting partition info for `fastboot getvar` when disk IO is busy.
119     disk: RefCell<D>,
120     partitions: RefCell<PartitionTable<G>>,
121     info_cache: BlockInfo,
122 }
123 
124 impl<B, S, T> GblDisk<Disk<B, S>, Gpt<T>>
125 where
126     B: BlockIo,
127     S: DerefMut<Target = [u8]>,
128     T: DerefMut<Target = [u8]>,
129 {
130     /// Creates a new instance as a GPT device.
new_gpt(mut disk: Disk<B, S>, gpt: Gpt<T>) -> Self131     pub fn new_gpt(mut disk: Disk<B, S>, gpt: Gpt<T>) -> Self {
132         let info_cache = disk.io().info();
133         Self { disk: disk.into(), info_cache, partitions: PartitionTable::Gpt(gpt).into() }
134     }
135 
136     /// Creates a new instance as a raw storage partition.
new_raw(mut disk: Disk<B, S>, name: &CStr) -> Result<Self, Error>137     pub fn new_raw(mut disk: Disk<B, S>, name: &CStr) -> Result<Self, Error> {
138         let info_cache = disk.io().info();
139         Ok(Self {
140             disk: disk.into(),
141             info_cache,
142             partitions: PartitionTable::Raw(RawName::new(name)?, info_cache.total_size()?).into(),
143         })
144     }
145 
146     /// Gets the cached `BlockInfo`.
block_info(&self) -> BlockInfo147     pub fn block_info(&self) -> BlockInfo {
148         self.info_cache
149     }
150 
151     /// Gets the block status.
status(&self) -> BlockStatus152     pub fn status(&self) -> BlockStatus {
153         match self.disk.try_borrow_mut().ok() {
154             None => BlockStatus::Pending,
155             _ => BlockStatus::Idle,
156         }
157     }
158 
159     /// Borrows disk mutably.
get_disk(&self) -> Result<RefMut<'_, Disk<B, S>>, Error>160     fn get_disk(&self) -> Result<RefMut<'_, Disk<B, S>>, Error> {
161         self.disk.try_borrow_mut().map_err(|_| Error::NotReady)
162     }
163 
164     /// Gets an instance of `PartitionIo` for a partition.
165     ///
166     /// If `part` is `None`, an IO for the whole block device is returned.
partition_io(&self, part: Option<&str>) -> Result<PartitionIo<'_, B>, Error>167     pub fn partition_io(&self, part: Option<&str>) -> Result<PartitionIo<'_, B>, Error> {
168         let (part_start, part_end) = self.find_partition(part)?.absolute_range()?;
169         Ok(PartitionIo { disk: Disk::from_ref_mut(self.get_disk()?), part_start, part_end })
170     }
171 
172     /// Finds a partition.
173     ///
174     /// * If `part` is none, the method returns an unnamed `Partition` that represents the whole
175     //    raw storage.
find_partition(&self, part: Option<&str>) -> Result<Partition, Error>176     pub fn find_partition(&self, part: Option<&str>) -> Result<Partition, Error> {
177         let Some(part) = part else {
178             return Ok(Partition::Raw(RawName::new(c"").unwrap(), self.info_cache.total_size()?));
179         };
180 
181         match self.partitions.try_borrow_mut().map_err(|_| Error::NotReady)?.deref() {
182             PartitionTable::Gpt(gpt) => Ok(Partition::Gpt(gpt.find_partition(part)?)),
183             PartitionTable::Raw(name, size) if name.to_str() == part => {
184                 Ok(Partition::Raw(*name, *size))
185             }
186             _ => Err(Error::NotFound),
187         }
188     }
189 
190     /// Get total number of partitions.
num_partitions(&self) -> Result<usize, Error>191     pub fn num_partitions(&self) -> Result<usize, Error> {
192         match self.partitions.try_borrow_mut().map_err(|_| Error::NotReady)?.deref() {
193             PartitionTable::Raw(_, _) => Ok(1),
194             PartitionTable::Gpt(gpt) => gpt.num_partitions(),
195         }
196     }
197 
198     /// Gets a partition by index.
get_partition_by_idx(&self, idx: usize) -> Result<Partition, Error>199     pub fn get_partition_by_idx(&self, idx: usize) -> Result<Partition, Error> {
200         match self.partitions.try_borrow_mut().map_err(|_| Error::NotReady)?.deref() {
201             PartitionTable::Raw(name, v) if idx == 0 => Ok(Partition::Raw(*name, *v)),
202             PartitionTable::Gpt(gpt) => Ok(Partition::Gpt(gpt.get_partition(idx)?)),
203             _ => Err(Error::InvalidInput),
204         }
205     }
206 
207     /// Syncs GPT if the partition type is GPT.
208     ///
209     /// # Returns
210     ///
211     /// * Returns `Ok(Some(sync_res))` if partition type is GPT and disk access is successful, where
212     ///  `sync_res` contains the GPT verification and restoration result.
213     /// * Returns `Ok(None)` if partition type is not GPT.
214     /// * Returns `Err` in other cases.
sync_gpt(&self) -> Result<Option<GptSyncResult>, Error>215     pub async fn sync_gpt(&self) -> Result<Option<GptSyncResult>, Error> {
216         match self.partitions.try_borrow_mut().map_err(|_| Error::NotReady)?.deref_mut() {
217             PartitionTable::Raw(_, _) => Ok(None),
218             PartitionTable::Gpt(ref mut gpt) => {
219                 let mut blk = self.disk.try_borrow_mut().map_err(|_| Error::NotReady)?;
220                 Ok(Some(blk.sync_gpt(gpt).await?))
221             }
222         }
223     }
224 
225     /// Updates GPT to the block device and sync primary and secondary GPT.
226     ///
227     /// # Args
228     ///
229     /// * `mbr_primary`: A buffer containing the MBR block, primary GPT header and entries.
230     /// * `resize`: If set to true, the method updates the last partition to cover the rest of the
231     ///    storage.
232     ///
233     /// # Returns
234     ///
235     /// * Return `Err(Error::NotReady)` if device is busy.
236     /// * Return `Err(Error::Unsupported)` if partition type is not GPT.
237     /// * Return `Ok(())` new GPT is valid and device is updated and synced successfully.
update_gpt(&self, mbr_primary: &mut [u8], resize: bool) -> Result<(), Error>238     pub async fn update_gpt(&self, mbr_primary: &mut [u8], resize: bool) -> Result<(), Error> {
239         match self.partitions.try_borrow_mut().map_err(|_| Error::NotReady)?.deref_mut() {
240             PartitionTable::Raw(_, _) => Err(Error::Unsupported),
241             PartitionTable::Gpt(ref mut gpt) => {
242                 let mut blk = self.disk.try_borrow_mut().map_err(|_| Error::NotReady)?;
243                 blk.update_gpt(mbr_primary, resize, gpt).await
244             }
245         }
246     }
247 
248     /// Erases GPT on the disk.
249     ///
250     /// # Returns
251     ///
252     /// * Return `Err(Error::NotReady)` if device is busy.
253     /// * Return `Err(Error::Unsupported)` if partition type is not GPT.
erase_gpt(&self) -> Result<(), Error>254     pub async fn erase_gpt(&self) -> Result<(), Error> {
255         match self.partitions.try_borrow_mut().map_err(|_| Error::NotReady)?.deref_mut() {
256             PartitionTable::Raw(_, _) => Err(Error::Unsupported),
257             PartitionTable::Gpt(ref mut gpt) => {
258                 let mut disk = self.disk.try_borrow_mut().map_err(|_| Error::NotReady)?;
259                 disk.erase_gpt(gpt).await
260             }
261         }
262     }
263 
264     /// Creates an instance of GptBuilder.
gpt_builder( &self, ) -> Result<GptBuilder<RefMut<'_, Disk<B, S>>, RefMut<'_, Gpt<T>>>, Error>265     pub fn gpt_builder(
266         &self,
267     ) -> Result<GptBuilder<RefMut<'_, Disk<B, S>>, RefMut<'_, Gpt<T>>>, Error> {
268         let mut parts = self.partitions.try_borrow_mut().map_err(|_| Error::NotReady)?;
269         match parts.deref_mut() {
270             PartitionTable::Raw(_, _) => Err(Error::Unsupported),
271             PartitionTable::Gpt(_) => {
272                 let gpt = RefMut::map(parts, |v| match v {
273                     PartitionTable::Gpt(v) => v,
274                     _ => unreachable!(),
275                 });
276                 Ok(GptBuilder::new(self.get_disk()?, gpt)?.0)
277             }
278         }
279     }
280 }
281 
282 /// `PartitionIo` provides read/write APIs to a partition.
283 pub struct PartitionIo<'a, B: BlockIo> {
284     disk: Disk<RefMut<'a, B>, RefMut<'a, [u8]>>,
285     part_start: u64,
286     part_end: u64,
287 }
288 
289 impl<'a, B: BlockIo> PartitionIo<'a, B> {
290     /// Returns the size of the partition.
size(&self) -> u64291     pub fn size(&self) -> u64 {
292         // Corrects by construction. Should not fail.
293         self.part_end.checked_sub(self.part_start).unwrap()
294     }
295 
296     /// Gets the block device.
dev(&mut self) -> &mut Disk<RefMut<'a, B>, RefMut<'a, [u8]>>297     pub fn dev(&mut self) -> &mut Disk<RefMut<'a, B>, RefMut<'a, [u8]>> {
298         &mut self.disk
299     }
300 
301     /// Checks the read/write parameters and returns the absolute offset in the block.
check_rw_range(&self, off: u64, size: impl Into<SafeNum>) -> Result<u64, Error>302     fn check_rw_range(&self, off: u64, size: impl Into<SafeNum>) -> Result<u64, Error> {
303         let ab_range_end = SafeNum::from(self.part_start) + off + size.into();
304         // Checks overflow by computing the difference between range end and partition end and
305         // making sure it succeeds.
306         (SafeNum::from(self.part_end) - ab_range_end)
307             .try_into()
308             .and_then(|_: u64| (SafeNum::from(self.part_start) + off).try_into())
309             .map_err(|_| Error::OutOfRange)
310     }
311 
312     /// Writes to the partition.
write(&mut self, off: u64, data: &mut [u8]) -> Result<(), Error>313     pub async fn write(&mut self, off: u64, data: &mut [u8]) -> Result<(), Error> {
314         self.disk.write(self.check_rw_range(off, data.len())?, data).await
315     }
316 
317     /// Reads from the partition.
read( &mut self, off: u64, out: &mut (impl SliceMaybeUninit + ?Sized), ) -> Result<(), Error>318     pub async fn read(
319         &mut self,
320         off: u64,
321         out: &mut (impl SliceMaybeUninit + ?Sized),
322     ) -> Result<(), Error> {
323         self.disk.read(self.check_rw_range(off, out.len())?, out).await
324     }
325 
326     /// Writes zeroes to the partition.
zeroize(&mut self, scratch: &mut [u8]) -> Result<(), Error>327     pub async fn zeroize(&mut self, scratch: &mut [u8]) -> Result<(), Error> {
328         self.disk.fill(self.part_start, self.size(), 0, scratch).await
329     }
330 
331     /// Writes sparse image to the partition.
write_sparse(&mut self, off: u64, img: &mut [u8]) -> Result<(), Error>332     pub async fn write_sparse(&mut self, off: u64, img: &mut [u8]) -> Result<(), Error> {
333         let sz = is_sparse_image(img).map_err(|_| Error::InvalidInput)?.data_size();
334         write_sparse_image(img, &mut (self.check_rw_range(off, sz)?, &mut self.disk)).await?;
335         Ok(())
336     }
337 
338     /// Turns this IO into one for a subrange in the partition.
sub(self, off: u64, sz: u64) -> Result<Self, Error>339     pub fn sub(self, off: u64, sz: u64) -> Result<Self, Error> {
340         self.check_rw_range(off, sz)?;
341         let mut sub = self;
342         sub.part_start += off;
343         sub.part_end = sub.part_start + sz;
344         Ok(sub)
345     }
346 }
347 
348 // Implements `SparseRawWriter` for tuple (<flash offset>, <block device>)
349 impl<B, S> SparseRawWriter for (u64, &mut Disk<B, S>)
350 where
351     B: BlockIo,
352     S: DerefMut<Target = [u8]>,
353 {
write(&mut self, off: u64, data: &mut [u8]) -> Result<(), Error>354     async fn write(&mut self, off: u64, data: &mut [u8]) -> Result<(), Error> {
355         Ok(self.1.write((SafeNum::from(off) + self.0).try_into()?, data).await?)
356     }
357 }
358 
359 /// Checks that a partition is unique.
360 ///
361 /// Returns a pair `(<block device index>, `Partition`)` if the partition exists and is unique.
check_part_unique( devs: &'_ [GblDisk< Disk<impl BlockIo, impl DerefMut<Target = [u8]>>, Gpt<impl DerefMut<Target = [u8]>>, >], part: &str, ) -> Result<(usize, Partition), Error>362 pub fn check_part_unique(
363     devs: &'_ [GblDisk<
364         Disk<impl BlockIo, impl DerefMut<Target = [u8]>>,
365         Gpt<impl DerefMut<Target = [u8]>>,
366     >],
367     part: &str,
368 ) -> Result<(usize, Partition), Error> {
369     let mut filtered = devs
370         .iter()
371         .enumerate()
372         .filter_map(|(i, v)| v.find_partition(Some(part)).ok().map(|v| (i, v)));
373     match (filtered.next(), filtered.next()) {
374         (Some(v), None) => Ok(v),
375         (Some(_), Some(_)) => Err(Error::NotUnique),
376         _ => Err(Error::NotFound),
377     }
378 }
379 
380 /// Checks that a partition is unique among all block devices and reads from it.
read_unique_partition( devs: &'_ [GblDisk< Disk<impl BlockIo, impl DerefMut<Target = [u8]>>, Gpt<impl DerefMut<Target = [u8]>>, >], part: &str, off: u64, out: &mut (impl SliceMaybeUninit + ?Sized), ) -> Result<(), Error>381 pub async fn read_unique_partition(
382     devs: &'_ [GblDisk<
383         Disk<impl BlockIo, impl DerefMut<Target = [u8]>>,
384         Gpt<impl DerefMut<Target = [u8]>>,
385     >],
386     part: &str,
387     off: u64,
388     out: &mut (impl SliceMaybeUninit + ?Sized),
389 ) -> Result<(), Error> {
390     devs[check_part_unique(devs, part)?.0].partition_io(Some(part))?.read(off, out).await
391 }
392 
393 /// Checks that a partition is unique among all block devices and writes to it.
write_unique_partition( devs: &'_ [GblDisk< Disk<impl BlockIo, impl DerefMut<Target = [u8]>>, Gpt<impl DerefMut<Target = [u8]>>, >], part: &str, off: u64, data: &mut [u8], ) -> Result<(), Error>394 pub async fn write_unique_partition(
395     devs: &'_ [GblDisk<
396         Disk<impl BlockIo, impl DerefMut<Target = [u8]>>,
397         Gpt<impl DerefMut<Target = [u8]>>,
398     >],
399     part: &str,
400     off: u64,
401     data: &mut [u8],
402 ) -> Result<(), Error> {
403     devs[check_part_unique(devs, part)?.0].partition_io(Some(part))?.write(off, data).await
404 }
405 
406 /// Syncs all GPT type partition devices.
sync_gpt( devs: &'_ [GblDisk< Disk<impl BlockIo, impl DerefMut<Target = [u8]>>, Gpt<impl DerefMut<Target = [u8]>>, >], ) -> Result<(), Error>407 pub async fn sync_gpt(
408     devs: &'_ [GblDisk<
409         Disk<impl BlockIo, impl DerefMut<Target = [u8]>>,
410         Gpt<impl DerefMut<Target = [u8]>>,
411     >],
412 ) -> Result<(), Error> {
413     for ele in &devs[..] {
414         ele.sync_gpt().await?;
415     }
416     Ok(())
417 }
418 
419 #[cfg(test)]
420 pub(crate) mod test {
421     use super::*;
422     use crate::ops::test::{FakeGblOpsStorage, TestGblDisk};
423     use core::fmt::Debug;
424     use gbl_async::block_on;
425 
426     /// Absolute start/end offset and size of "boot_a/b" partitions in
427     /// "../../libstorage/test/gpt_test_1.bin"
428     const BOOT_A_OFF: u64 = 17 * 1024;
429     const BOOT_A_END: u64 = 25 * 1024;
430     const BOOT_A_SZ: u64 = BOOT_A_END - BOOT_A_OFF;
431     const BOOT_B_OFF: u64 = 25 * 1024;
432     const BOOT_B_END: u64 = 37 * 1024;
433     const BOOT_B_SZ: u64 = BOOT_B_END - BOOT_B_OFF;
434     /// Total size of disk "../../libstorage/test/gpt_test_1.bin"
435     const GPT_DISK_1_SZ: u64 = 64 * 1024;
436 
437     /// A helper to convert an integer into usize and panics on error.
to_usize(val: impl TryInto<usize, Error = impl Debug>) -> usize438     fn to_usize(val: impl TryInto<usize, Error = impl Debug>) -> usize {
439         val.try_into().unwrap()
440     }
441 
442     /// A helper to create a GPT type TestGblDisk
gpt_disk(data: impl AsRef<[u8]>) -> TestGblDisk443     fn gpt_disk(data: impl AsRef<[u8]>) -> TestGblDisk {
444         let mut res = FakeGblOpsStorage::default();
445         res.add_gpt_device(data);
446         res.0.pop().unwrap()
447     }
448 
449     /// A helper to create a raw disk partition type TestGblDisk
raw_disk(name: &CStr, data: impl AsRef<[u8]>) -> TestGblDisk450     fn raw_disk(name: &CStr, data: impl AsRef<[u8]>) -> TestGblDisk {
451         let mut res = FakeGblOpsStorage::default();
452         res.add_raw_device(name, data);
453         res.0.pop().unwrap()
454     }
455 
456     #[test]
test_find_partition_gpt()457     fn test_find_partition_gpt() {
458         let gpt = gpt_disk(include_bytes!("../../libstorage/test/gpt_test_1.bin"));
459         assert_eq!(block_on(gpt.sync_gpt()).unwrap(), Some(GptSyncResult::BothValid));
460 
461         let boot_a = gpt.find_partition(Some("boot_a")).unwrap();
462         assert_eq!(boot_a.name().unwrap(), "boot_a");
463         assert_eq!(boot_a.size().unwrap(), BOOT_A_SZ);
464         assert_eq!(boot_a.absolute_range().unwrap(), (BOOT_A_OFF, BOOT_A_END));
465 
466         let boot_b = gpt.find_partition(Some("boot_b")).unwrap();
467         assert_eq!(boot_b.name().unwrap(), "boot_b");
468         assert_eq!(boot_b.size().unwrap(), BOOT_B_SZ);
469         assert_eq!(boot_b.absolute_range().unwrap(), (BOOT_B_OFF, BOOT_B_END));
470 
471         let unnamed_whole = gpt.find_partition(None).unwrap();
472         assert_eq!(unnamed_whole.name().unwrap(), "");
473         assert_eq!(unnamed_whole.size().unwrap(), GPT_DISK_1_SZ);
474         assert_eq!(unnamed_whole.absolute_range().unwrap(), (0, GPT_DISK_1_SZ));
475 
476         assert!(gpt.find_partition(Some("not-exist")).is_err());
477     }
478 
479     #[test]
test_find_partition_raw()480     fn test_find_partition_raw() {
481         let disk = include_bytes!("../../libstorage/test/gpt_test_1.bin");
482         let raw = raw_disk(c"raw", &disk);
483 
484         let raw_part = raw.find_partition(Some("raw")).unwrap();
485         assert_eq!(raw_part.name().unwrap(), "raw");
486         assert_eq!(raw_part.size().unwrap(), GPT_DISK_1_SZ);
487         assert_eq!(raw_part.absolute_range().unwrap(), (0, GPT_DISK_1_SZ));
488 
489         let unnamed_whole = raw.find_partition(None).unwrap();
490         assert_eq!(unnamed_whole.name().unwrap(), "");
491         assert_eq!(unnamed_whole.size().unwrap(), GPT_DISK_1_SZ);
492         assert_eq!(unnamed_whole.absolute_range().unwrap(), (0, GPT_DISK_1_SZ));
493 
494         assert!(raw.find_partition(Some("boot_a")).is_err());
495     }
496 
497     /// A helper for testing partition read.
498     ///
499     /// Tests that the content read at `off..off+sz` is the same as `part_content[off..off+sz]`.
test_part_read( blk: &TestGblDisk, part: Option<&str>, part_content: &[u8], off: u64, sz: u64, )500     fn test_part_read(
501         blk: &TestGblDisk,
502         part: Option<&str>,
503         part_content: &[u8],
504         off: u64,
505         sz: u64,
506     ) {
507         let mut out = vec![0u8; to_usize(sz)];
508         block_on(blk.partition_io(part).unwrap().read(off, &mut out[..])).unwrap();
509         assert_eq!(out, part_content[to_usize(off)..][..out.len()].to_vec());
510 
511         // Reads using the `sub()` and then read approach.
512         let mut out = vec![0u8; to_usize(sz)];
513         let mut io = blk.partition_io(part).unwrap().sub(off, sz).unwrap();
514         block_on(io.read(0, &mut out[..])).unwrap();
515         assert_eq!(out, part_content[to_usize(off)..][..out.len()].to_vec());
516     }
517 
518     #[test]
test_read_partition_gpt()519     fn test_read_partition_gpt() {
520         let disk = include_bytes!("../../libstorage/test/gpt_test_1.bin");
521         let gpt = gpt_disk(&disk[..]);
522         assert_eq!(block_on(gpt.sync_gpt()).unwrap(), Some(GptSyncResult::BothValid));
523 
524         let expect_boot_a = include_bytes!("../../libstorage/test/boot_a.bin");
525         test_part_read(&gpt, Some("boot_a"), expect_boot_a, 1, 1024);
526         let expect_boot_b = include_bytes!("../../libstorage/test/boot_b.bin");
527         test_part_read(&gpt, Some("boot_b"), expect_boot_b, 1, 1024);
528         // Whole block read.
529         test_part_read(&gpt, None, disk, 1, 1024);
530     }
531 
532     #[test]
test_read_partition_raw()533     fn test_read_partition_raw() {
534         let disk = include_bytes!("../../libstorage/test/gpt_test_1.bin");
535         let raw = raw_disk(c"raw", &disk);
536         test_part_read(&raw, Some("raw"), disk, 1, 1024);
537         test_part_read(&raw, None, disk, 1, 1024);
538     }
539 
540     /// A helper for testing partition write.
test_part_write(blk: &TestGblDisk, part: Option<&str>, off: u64, sz: u64)541     fn test_part_write(blk: &TestGblDisk, part: Option<&str>, off: u64, sz: u64) {
542         // Reads the current partition content
543         let mut part_content = vec![0u8; to_usize(blk.partition_io(part).unwrap().size())];
544         block_on(blk.partition_io(part).unwrap().read(0, &mut part_content[..])).unwrap();
545 
546         // Flips all the bits in the target range and writes back.
547         let seg = &mut part_content[to_usize(off)..][..to_usize(sz)];
548         seg.iter_mut().for_each(|v| *v = !(*v));
549         block_on(blk.partition_io(part).unwrap().write(off, seg)).unwrap();
550         // Checks that data is written.
551         test_part_read(blk, part, &part_content, off, sz);
552 
553         // Writes using the `sub()` and then write approach.
554         let seg = &mut part_content[to_usize(off)..][..to_usize(sz)];
555         seg.iter_mut().for_each(|v| *v = !(*v));
556         block_on(blk.partition_io(part).unwrap().sub(off, sz).unwrap().write(0, seg)).unwrap();
557         test_part_read(blk, part, &part_content, off, sz);
558     }
559 
560     #[test]
test_write_partition_gpt()561     fn test_write_partition_gpt() {
562         let gpt = gpt_disk(include_bytes!("../../libstorage/test/gpt_test_1.bin"));
563         assert_eq!(block_on(gpt.sync_gpt()).unwrap(), Some(GptSyncResult::BothValid));
564         test_part_write(&gpt, Some("boot_a"), 1, 1024);
565         test_part_write(&gpt, Some("boot_b"), 1, 1024);
566         test_part_write(&gpt, None, 1, 1024);
567     }
568 
569     #[test]
test_write_partition_raw()570     fn test_write_partition_raw() {
571         let mut raw = raw_disk(c"raw", include_bytes!("../../libstorage/test/gpt_test_1.bin"));
572         test_part_write(&mut raw, Some("raw"), 1, 1024);
573         test_part_write(&mut raw, None, 1, 1024);
574     }
575 
576     #[test]
test_read_write_partition_overflow()577     fn test_read_write_partition_overflow() {
578         let disk = include_bytes!("../../libstorage/test/gpt_test_1.bin");
579         let gpt = gpt_disk(&disk[..]);
580         assert_eq!(block_on(gpt.sync_gpt()).unwrap(), Some(GptSyncResult::BothValid));
581 
582         let mut part_io = gpt.partition_io(Some("boot_a")).unwrap();
583         assert!(block_on(part_io.read(BOOT_A_END, &mut vec![0u8; 1][..])).is_err());
584         assert!(block_on(part_io.read(BOOT_A_OFF, &mut vec![0u8; to_usize(BOOT_A_SZ) + 1][..]))
585             .is_err());
586         assert!(block_on(part_io.write(BOOT_A_END, &mut vec![0u8; 1][..])).is_err());
587         assert!(block_on(part_io.write(BOOT_A_OFF, &mut vec![0u8; to_usize(BOOT_A_SZ) + 1][..]))
588             .is_err());
589 
590         let raw = raw_disk(c"raw", &disk);
591         let mut part_io = raw.partition_io(Some("raw")).unwrap();
592         assert!(block_on(part_io.read(GPT_DISK_1_SZ, &mut vec![0u8; 1][..])).is_err());
593         assert!(block_on(part_io.read(0, &mut vec![0u8; to_usize(GPT_DISK_1_SZ) + 1][..])).is_err());
594         assert!(block_on(part_io.write(GPT_DISK_1_SZ, &mut vec![0u8; 1][..])).is_err());
595         assert!(
596             block_on(part_io.write(0, &mut vec![0u8; to_usize(GPT_DISK_1_SZ) + 1][..])).is_err()
597         );
598     }
599 
600     #[test]
test_sub_overflow()601     fn test_sub_overflow() {
602         let disk = include_bytes!("../../libstorage/test/gpt_test_1.bin");
603         let gpt = gpt_disk(&disk[..]);
604         assert_eq!(block_on(gpt.sync_gpt()).unwrap(), Some(GptSyncResult::BothValid));
605         assert!(gpt.partition_io(Some("boot_a")).unwrap().sub(0, BOOT_A_SZ + 1).is_err());
606         assert!(gpt.partition_io(Some("boot_a")).unwrap().sub(1, BOOT_A_SZ).is_err());
607 
608         let raw = raw_disk(c"raw", &disk);
609         assert!(raw.partition_io(Some("raw")).unwrap().sub(0, GPT_DISK_1_SZ + 1).is_err());
610         assert!(raw.partition_io(Some("raw")).unwrap().sub(1, GPT_DISK_1_SZ).is_err());
611     }
612 
613     #[test]
test_write_sparse()614     fn test_write_sparse() {
615         let sparse_raw = include_bytes!("../testdata/sparse_test_raw.bin");
616         let mut sparse = include_bytes!("../testdata/sparse_test.bin").to_vec();
617         let raw = &vec![0u8; sparse_raw.len() + 512][..];
618         let blk = raw_disk(c"raw", raw);
619         block_on(
620             blk.partition_io(Some("raw"))
621                 .unwrap()
622                 .sub(1, u64::try_from(raw.len() - 1).unwrap())
623                 .unwrap()
624                 .write_sparse(1, &mut sparse),
625         )
626         .unwrap();
627         let mut expected = vec![0u8; raw.len()];
628         expected[1 + 1..][..sparse_raw.len()].clone_from_slice(sparse_raw);
629         test_part_read(&blk, Some("raw"), &expected, 1, sparse_raw.len().try_into().unwrap());
630     }
631 
632     #[test]
test_write_sparse_not_sparse_image()633     fn test_write_sparse_not_sparse_image() {
634         let sparse_raw = include_bytes!("../testdata/sparse_test_raw.bin");
635         let mut sparse = include_bytes!("../testdata/sparse_test.bin").to_vec();
636         sparse[0] = !sparse[0]; // Corrupt image.
637         let raw = raw_disk(c"raw", vec![0u8; sparse_raw.len() + 512]);
638         assert!(
639             block_on(raw.partition_io(Some("raw")).unwrap().write_sparse(1, &mut sparse)).is_err()
640         );
641     }
642 
643     #[test]
test_write_sparse_overflow_size()644     fn test_write_sparse_overflow_size() {
645         let sparse_raw = include_bytes!("../testdata/sparse_test_raw.bin");
646         let mut sparse = include_bytes!("../testdata/sparse_test.bin").to_vec();
647         let raw = raw_disk(c"raw", vec![0u8; sparse_raw.len()]);
648         assert!(
649             block_on(raw.partition_io(Some("raw")).unwrap().write_sparse(1, &mut sparse)).is_err()
650         );
651     }
652 
653     #[test]
test_partition_iter()654     fn test_partition_iter() {
655         let raw = raw_disk(c"raw", vec![0u8; 1024]);
656         assert_eq!(raw.num_partitions().unwrap(), 1);
657         assert_eq!(raw.get_partition_by_idx(0).unwrap().name(), Ok("raw"));
658         assert_eq!(raw.get_partition_by_idx(0).unwrap().size(), Ok(1024));
659 
660         let gpt = gpt_disk(include_bytes!("../../libstorage/test/gpt_test_1.bin"));
661         block_on(gpt.sync_gpt()).unwrap();
662         assert_eq!(gpt.num_partitions().unwrap(), 2);
663         assert_eq!(gpt.get_partition_by_idx(0).unwrap().name().unwrap(), "boot_a");
664         assert_eq!(gpt.get_partition_by_idx(0).unwrap().size().unwrap(), 0x2000);
665         assert_eq!(gpt.get_partition_by_idx(1).unwrap().name().unwrap(), "boot_b");
666         assert_eq!(gpt.get_partition_by_idx(1).unwrap().size().unwrap(), 0x3000);
667     }
668 
669     /// A test helper for `read_unique_partition`
670     /// It verifies that data read from partition `part` at offset `off` is the same as
671     /// `part_content[off..off+sz]`.
check_read_partition( devs: &[TestGblDisk], part: &str, part_content: &[u8], off: u64, sz: u64, )672     fn check_read_partition(
673         devs: &[TestGblDisk],
674         part: &str,
675         part_content: &[u8],
676         off: u64,
677         sz: u64,
678     ) {
679         let mut out = vec![0u8; to_usize(sz)];
680         block_on(read_unique_partition(devs, part, off, &mut out[..])).unwrap();
681         assert_eq!(out, part_content[to_usize(off)..][..out.len()]);
682     }
683 
684     #[test]
test_read_unique_partition()685     fn test_read_unique_partition() {
686         let mut devs = FakeGblOpsStorage::default();
687         devs.add_gpt_device(include_bytes!("../../libstorage/test/gpt_test_1.bin"));
688         devs.add_gpt_device(include_bytes!("../../libstorage/test/gpt_test_2.bin"));
689         devs.add_raw_device(c"raw_0", [0xaau8; 4 * 1024]);
690         devs.add_raw_device(c"raw_1", [0x55u8; 4 * 1024]);
691 
692         let boot_a = include_bytes!("../../libstorage/test/boot_a.bin");
693         let boot_b = include_bytes!("../../libstorage/test/boot_b.bin");
694 
695         let off = 512u64;
696         let sz = 1024u64;
697         check_read_partition(&mut devs, "boot_a", boot_a, off, sz);
698         check_read_partition(&mut devs, "boot_b", boot_b, off, sz);
699 
700         let vendor_boot_a = include_bytes!("../../libstorage/test/vendor_boot_a.bin");
701         let vendor_boot_b = include_bytes!("../../libstorage/test/vendor_boot_b.bin");
702 
703         check_read_partition(&mut devs, "vendor_boot_a", vendor_boot_a, off, sz);
704         check_read_partition(&mut devs, "vendor_boot_b", vendor_boot_b, off, sz);
705 
706         check_read_partition(&mut devs, "raw_0", &[0xaau8; 4 * 1024][..], off, sz);
707         check_read_partition(&mut devs, "raw_1", &[0x55u8; 4 * 1024][..], off, sz);
708     }
709 
710     /// A test helper for `write_unique_partition`
check_write_partition(devs: &[TestGblDisk], part: &str, off: u64, sz: u64)711     fn check_write_partition(devs: &[TestGblDisk], part: &str, off: u64, sz: u64) {
712         // Reads the current partition content
713         let (_, p) = check_part_unique(devs, part).unwrap();
714         let mut part_content = vec![0u8; to_usize(p.size().unwrap())];
715         block_on(read_unique_partition(devs, part, 0, &mut part_content[..])).unwrap();
716 
717         // Flips all the bits in the target range and writes back.
718         let seg = &mut part_content[to_usize(off)..][..to_usize(sz)];
719         seg.iter_mut().for_each(|v| *v = !(*v));
720         block_on(write_unique_partition(devs, part, off, seg)).unwrap();
721         // Checks that data is written.
722         check_read_partition(devs, part, &part_content, off, sz);
723     }
724 
725     #[test]
test_write_unique_partition()726     fn test_write_unique_partition() {
727         let mut devs = FakeGblOpsStorage::default();
728         devs.add_gpt_device(include_bytes!("../../libstorage/test/gpt_test_1.bin"));
729         devs.add_gpt_device(include_bytes!("../../libstorage/test/gpt_test_2.bin"));
730         devs.add_raw_device(c"raw_0", [0xaau8; 4 * 1024]);
731         devs.add_raw_device(c"raw_1", [0x55u8; 4 * 1024]);
732 
733         let off = 512u64;
734         let sz = 1024u64;
735         check_write_partition(&mut devs, "boot_a", off, sz);
736         check_write_partition(&mut devs, "boot_b", off, sz);
737         check_write_partition(&mut devs, "vendor_boot_a", off, sz);
738         check_write_partition(&mut devs, "vendor_boot_b", off, sz);
739         check_write_partition(&mut devs, "raw_0", off, sz);
740         check_write_partition(&mut devs, "raw_1", off, sz);
741     }
742 
743     #[test]
test_rw_fail_with_non_unique_partition()744     fn test_rw_fail_with_non_unique_partition() {
745         let mut devs = FakeGblOpsStorage::default();
746         devs.add_gpt_device(include_bytes!("../../libstorage/test/gpt_test_1.bin"));
747         devs.add_gpt_device(include_bytes!("../../libstorage/test/gpt_test_1.bin"));
748         devs.add_raw_device(c"raw", [0xaau8; 4 * 1024]);
749         devs.add_raw_device(c"raw", [0x55u8; 4 * 1024]);
750 
751         assert!(block_on(read_unique_partition(&devs, "boot_a", 0, &mut [] as &mut [u8],)).is_err());
752         assert!(block_on(write_unique_partition(&devs, "boot_a", 0, &mut [],)).is_err());
753         assert!(block_on(read_unique_partition(&devs, "raw", 0, &mut [] as &mut [u8],)).is_err());
754         assert!(block_on(write_unique_partition(&devs, "raw", 0, &mut [],)).is_err());
755     }
756 }
757