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