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