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