1 // Copyright 2024, The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 use crate::{AsBlockDevice, BlockIo, Partition, Result, StorageError};
16 
17 /// `AsMultiBlockDevices` provides APIs for finding/reading/writing raw data or GPT partitions from
18 /// multiple block devices.
19 pub trait AsMultiBlockDevices {
20     /// Calls closure `f` for each `AsBlockDevice` object and its unique `id` until reaching end.
for_each( &mut self, f: &mut dyn FnMut(&mut dyn AsBlockDevice, u64), ) -> core::result::Result<(), Option<&'static str>>21     fn for_each(
22         &mut self,
23         f: &mut dyn FnMut(&mut dyn AsBlockDevice, u64),
24     ) -> core::result::Result<(), Option<&'static str>>;
25 
26     /// Calls closure `f` for each `AsBlockDevice` object and its unique `id` until reaching end of
27     /// returnning true.
for_each_until( &mut self, f: &mut dyn FnMut(&mut dyn AsBlockDevice, u64) -> bool, ) -> Result<()>28     fn for_each_until(
29         &mut self,
30         f: &mut dyn FnMut(&mut dyn AsBlockDevice, u64) -> bool,
31     ) -> Result<()> {
32         let mut stop = false;
33         self.for_each(&mut |io, id| {
34             stop = stop || f(io, id);
35         })
36         .map_err(|v| StorageError::FailedGettingBlockDevices(v))
37     }
38 
39     /// Gets the block device with the given id.
get(&mut self, id: u64) -> Result<SelectedBlockDevice> where Self: Sized,40     fn get(&mut self, id: u64) -> Result<SelectedBlockDevice>
41     where
42         Self: Sized,
43     {
44         with_id(self, id, |_| {})?;
45         Ok(SelectedBlockDevice { devs: self, id: id })
46     }
47 
48     /// Syncs gpt for all block devices. Caller provides a callback for handling sync error for
49     /// each block device.
sync_gpt_all(&mut self, f: &mut dyn FnMut(&mut dyn AsBlockDevice, u64, StorageError))50     fn sync_gpt_all(&mut self, f: &mut dyn FnMut(&mut dyn AsBlockDevice, u64, StorageError)) {
51         let _ = self.for_each_until(&mut |v, id| {
52             match v.sync_gpt() {
53                 Err(e) => f(v, id, e),
54                 _ => {}
55             }
56             false
57         });
58     }
59 
60     /// Checks that a partition exists and is unique among all block devices with GPT.
61     ///
62     /// Returns the block device ID for the partition.
check_part(&mut self, part: &str) -> Result<(u64, Partition)>63     fn check_part(&mut self, part: &str) -> Result<(u64, Partition)> {
64         let mut res = Err(StorageError::NotExist);
65         self.for_each_until(&mut |v, id| {
66             res = match v.find_partition(part).map(|v| (id, v)) {
67                 Ok(_) if res.is_ok() => Err(StorageError::PartitionNotUnique),
68                 v => v.or(res),
69             };
70             res.err() == Some(StorageError::PartitionNotUnique)
71         })?;
72         res
73     }
74 
75     /// Returns the block size and `GptEntry` for a partition.
76     ///
77     /// Returns Ok(()) if the partition is found and unique among all block devices.
find_partition(&mut self, part: &str) -> Result<Partition>78     fn find_partition(&mut self, part: &str) -> Result<Partition> {
79         self.check_part(part)?;
80         until_ok(self, |dev, _| dev.find_partition(part))
81     }
82 
83     /// Reads a GPT partition.
84     ///
85     /// Returns Ok(()) if the partition is unique among all block devices and read is successful.
read_gpt_partition(&mut self, part_name: &str, offset: u64, out: &mut [u8]) -> Result<()>86     fn read_gpt_partition(&mut self, part_name: &str, offset: u64, out: &mut [u8]) -> Result<()> {
87         self.check_part(part_name)?;
88         until_ok(self, |dev, _| dev.read_gpt_partition(part_name, offset, out))
89     }
90 
91     /// Writes a GPT partition with mutable input buffer.
92     ///
93     /// Returns Ok(()) if the partition is unique among all block devices and write is successful.
write_gpt_partition(&mut self, part_name: &str, offset: u64, data: &mut [u8]) -> Result<()>94     fn write_gpt_partition(&mut self, part_name: &str, offset: u64, data: &mut [u8]) -> Result<()> {
95         self.check_part(part_name)?;
96         until_ok(self, |dev, _| dev.write_gpt_partition(part_name, offset, &mut data[..]))
97     }
98 }
99 
100 impl<T: ?Sized + AsMultiBlockDevices> AsMultiBlockDevices for &mut T {
for_each( &mut self, f: &mut dyn FnMut(&mut dyn AsBlockDevice, u64), ) -> core::result::Result<(), Option<&'static str>>101     fn for_each(
102         &mut self,
103         f: &mut dyn FnMut(&mut dyn AsBlockDevice, u64),
104     ) -> core::result::Result<(), Option<&'static str>> {
105         (*self).for_each(&mut |io, id| f(io, id))
106     }
107 }
108 
109 /// Iterates and runs a closure on each block device until `Ok(R)` is returned.
until_ok<F, R>(devs: &mut (impl AsMultiBlockDevices + ?Sized), mut f: F) -> Result<R> where F: FnMut(&mut dyn AsBlockDevice, u64) -> Result<R>,110 fn until_ok<F, R>(devs: &mut (impl AsMultiBlockDevices + ?Sized), mut f: F) -> Result<R>
111 where
112     F: FnMut(&mut dyn AsBlockDevice, u64) -> Result<R>,
113 {
114     let mut res: Result<R> = Err(StorageError::BlockDeviceNotFound);
115     devs.for_each_until(&mut |v, id| {
116         res = f(v, id);
117         res.is_ok()
118     })?;
119     res
120 }
121 
122 /// Finds the first block device with the given ID and runs a closure with it.
with_id<F, R>(devs: &mut (impl AsMultiBlockDevices + ?Sized), dev_id: u64, mut f: F) -> Result<R> where F: FnMut(&mut dyn AsBlockDevice) -> R,123 fn with_id<F, R>(devs: &mut (impl AsMultiBlockDevices + ?Sized), dev_id: u64, mut f: F) -> Result<R>
124 where
125     F: FnMut(&mut dyn AsBlockDevice) -> R,
126 {
127     until_ok(devs, |dev, id| match dev_id == id {
128         true => Ok(f(dev)),
129         _ => Err(StorageError::BlockDeviceNotFound),
130     })
131 }
132 
133 /// `SelectedBlockDevice` is returned by `AsMultiBlockDevices::get()` and provides access to
134 /// the `AsBlockDevice` object of the given id.
135 pub struct SelectedBlockDevice<'a> {
136     devs: &'a mut dyn AsMultiBlockDevices,
137     id: u64,
138 }
139 
140 impl AsBlockDevice for SelectedBlockDevice<'_> {
with(&mut self, f: &mut dyn FnMut(&mut dyn BlockIo, &mut [u8], u64))141     fn with(&mut self, f: &mut dyn FnMut(&mut dyn BlockIo, &mut [u8], u64)) {
142         let _ = with_id(self.devs, self.id, |dev| dev.with(f));
143     }
144 }
145 
146 #[cfg(test)]
147 mod test {
148     use gbl_storage_testlib::{
149         AsBlockDevice, AsMultiBlockDevices, TestBlockDeviceBuilder, TestMultiBlockDevices,
150     };
151 
152     #[test]
test_get()153     fn test_get() {
154         let mut devs: TestMultiBlockDevices = TestMultiBlockDevices(vec![
155             include_bytes!("../test/gpt_test_1.bin").as_slice().into(),
156             include_bytes!("../test/gpt_test_2.bin").as_slice().into(),
157         ]);
158         devs.sync_gpt_all(&mut |_, _, _| panic!("GPT sync failed"));
159         devs.get(0).unwrap();
160         devs.get(1).unwrap();
161         assert!(devs.get(2).is_err());
162     }
163 
164     #[test]
test_multi_block_read()165     fn test_multi_block_read() {
166         let off = 512; // Randomly selected offset.
167         let blk_0 = include_bytes!("../test/gpt_test_1.bin");
168         let blk_1 = include_bytes!("../test/gpt_test_2.bin");
169         let mut devs =
170             TestMultiBlockDevices(vec![blk_0.as_slice().into(), blk_1.as_slice().into()]);
171 
172         let mut out = vec![0u8; blk_0[off..].len()];
173         devs.get(0).unwrap().read(u64::try_from(off).unwrap(), &mut out[..]).unwrap();
174         assert_eq!(out, blk_0[off..]);
175 
176         let mut out = vec![0u8; blk_1[off..].len()];
177         devs.get(1).unwrap().read(u64::try_from(off).unwrap(), &mut out[..]).unwrap();
178         assert_eq!(out, blk_1[off..]);
179     }
180 
181     #[test]
test_multi_block_write()182     fn test_multi_block_write() {
183         let off = 512; // Randomly selected offset.
184         let mut blk_0 = include_bytes!("../test/gpt_test_1.bin").to_vec();
185         let mut blk_1 = include_bytes!("../test/gpt_test_2.bin").to_vec();
186         let mut devs = TestMultiBlockDevices(vec![
187             TestBlockDeviceBuilder::new().set_size(blk_0.len()).build(),
188             TestBlockDeviceBuilder::new().set_size(blk_1.len()).build(),
189         ]);
190 
191         devs.get(0).unwrap().write(u64::try_from(off).unwrap(), &mut blk_0[off..]).unwrap();
192         assert_eq!(blk_0[off..], devs.0[0].io.storage[off..]);
193 
194         devs.get(1).unwrap().write(u64::try_from(off).unwrap(), &mut blk_1[off..]).unwrap();
195         assert_eq!(blk_1[off..], devs.0[1].io.storage[off..]);
196     }
197 
198     #[test]
test_multi_block_gpt_partition_size()199     fn test_multi_block_gpt_partition_size() {
200         let mut devs = TestMultiBlockDevices(vec![
201             include_bytes!("../test/gpt_test_1.bin").as_slice().into(),
202             include_bytes!("../test/gpt_test_2.bin").as_slice().into(),
203         ]);
204         devs.sync_gpt_all(&mut |_, _, _| panic!("GPT sync failed"));
205 
206         assert_eq!(devs.find_partition("boot_a").map(|v| v.size()).unwrap(), Ok(8 * 1024));
207         assert_eq!(
208             devs.get(0).unwrap().find_partition("boot_a").map(|v| v.size()).unwrap(),
209             Ok(8 * 1024)
210         );
211 
212         assert_eq!(devs.find_partition("boot_b").map(|v| v.size()).unwrap(), Ok(12 * 1024));
213         assert_eq!(
214             devs.get(0).unwrap().find_partition("boot_b").map(|v| v.size()).unwrap(),
215             Ok(12 * 1024)
216         );
217 
218         assert_eq!(devs.find_partition("vendor_boot_a").map(|v| v.size()).unwrap(), Ok(4 * 1024));
219         assert_eq!(
220             devs.get(1).unwrap().find_partition("vendor_boot_a").map(|v| v.size()).unwrap(),
221             Ok(4 * 1024)
222         );
223 
224         assert_eq!(devs.find_partition("vendor_boot_b").map(|v| v.size()).unwrap(), Ok(6 * 1024));
225         assert_eq!(
226             devs.get(1).unwrap().find_partition("vendor_boot_b").map(|v| v.size()).unwrap(),
227             Ok(6 * 1024)
228         );
229     }
230 
231     /// A test helper for `AsMultiBlockDevices::read_gpt_partition`
232     /// It verifies that data read partition `part` at offset `off` is the same as
233     /// `expected[off..]`.
check_read_partition( devs: &mut TestMultiBlockDevices, part: &str, off: u64, part_data: &[u8], )234     fn check_read_partition(
235         devs: &mut TestMultiBlockDevices,
236         part: &str,
237         off: u64,
238         part_data: &[u8],
239     ) {
240         let expected = &part_data[off.try_into().unwrap()..];
241         let mut out = vec![0u8; expected.len()];
242         devs.read_gpt_partition(part, off, &mut out[..]).unwrap();
243         assert_eq!(out, expected.to_vec());
244     }
245 
246     #[test]
test_multi_block_gpt_read()247     fn test_multi_block_gpt_read() {
248         let off = 512u64; // Randomly selected offset.
249 
250         let mut devs = TestMultiBlockDevices(vec![
251             include_bytes!("../test/gpt_test_1.bin").as_slice().into(),
252             include_bytes!("../test/gpt_test_2.bin").as_slice().into(),
253         ]);
254         devs.sync_gpt_all(&mut |_, _, _| panic!("GPT sync failed"));
255 
256         let expect_boot_a = include_bytes!("../test/boot_a.bin");
257         let expect_boot_b = include_bytes!("../test/boot_b.bin");
258 
259         check_read_partition(&mut devs, "boot_a", off, expect_boot_a);
260         check_read_partition(&mut devs, "boot_b", off, expect_boot_b);
261 
262         let expect_vendor_boot_a = include_bytes!("../test/vendor_boot_a.bin");
263         let expect_vendor_boot_b = include_bytes!("../test/vendor_boot_b.bin");
264 
265         check_read_partition(&mut devs, "vendor_boot_a", off, expect_vendor_boot_a);
266         check_read_partition(&mut devs, "vendor_boot_b", off, expect_vendor_boot_b);
267     }
268 
269     /// A test helper for `AsMultiBlockDevices::write_gpt_partition`
270     /// It verifies that `data[off..]` is correctly written to partition `part` at offset `off`.
check_write_partition( devs: &mut TestMultiBlockDevices, part: &str, off: u64, data: &mut [u8], )271     fn check_write_partition(
272         devs: &mut TestMultiBlockDevices,
273         part: &str,
274         off: u64,
275         data: &mut [u8],
276     ) {
277         let to_write = &mut data[off.try_into().unwrap()..];
278 
279         let mut out = vec![0u8; to_write.len()];
280         devs.write_gpt_partition(part, off, to_write).unwrap();
281         devs.read_gpt_partition(part, off, &mut out[..]).unwrap();
282         assert_eq!(out, to_write.to_vec());
283 
284         to_write.reverse();
285         devs.write_gpt_partition(part, off, to_write).unwrap();
286         devs.read_gpt_partition(part, off, &mut out[..]).unwrap();
287         assert_eq!(out, to_write.to_vec());
288     }
289 
290     #[test]
test_multi_block_gpt_write()291     fn test_multi_block_gpt_write() {
292         let off = 512u64; // Randomly selected offset.
293 
294         let mut devs = TestMultiBlockDevices(vec![
295             include_bytes!("../test/gpt_test_1.bin").as_slice().into(),
296             include_bytes!("../test/gpt_test_2.bin").as_slice().into(),
297         ]);
298         devs.sync_gpt_all(&mut |_, _, _| panic!("GPT sync failed"));
299 
300         let expect_boot_a = &mut include_bytes!("../test/boot_a.bin").to_vec();
301         let expect_boot_b = &mut include_bytes!("../test/boot_b.bin").to_vec();
302 
303         expect_boot_a.reverse();
304         expect_boot_b.reverse();
305         check_write_partition(&mut devs, "boot_a", off, expect_boot_a);
306         check_write_partition(&mut devs, "boot_b", off, expect_boot_b);
307 
308         let expect_vendor_boot_a = &mut include_bytes!("../test/vendor_boot_a.bin").to_vec();
309         let expect_vendor_boot_b = &mut include_bytes!("../test/vendor_boot_b.bin").to_vec();
310 
311         expect_boot_a.reverse();
312         expect_boot_b.reverse();
313         check_write_partition(&mut devs, "vendor_boot_a", off, expect_vendor_boot_a);
314         check_write_partition(&mut devs, "vendor_boot_b", off, expect_vendor_boot_b);
315     }
316 
317     #[test]
test_none_block_id_fail_with_non_unique_partition()318     fn test_none_block_id_fail_with_non_unique_partition() {
319         let mut devs = TestMultiBlockDevices(vec![
320             include_bytes!("../test/gpt_test_1.bin").as_slice().into(),
321             include_bytes!("../test/gpt_test_1.bin").as_slice().into(),
322         ]);
323         devs.sync_gpt_all(&mut |_, _, _| panic!("GPT sync failed"));
324         assert!(devs.read_gpt_partition("boot_a", 0, &mut []).is_err());
325         assert!(devs.write_gpt_partition("boot_a", 0, &mut []).is_err());
326         assert!(devs.find_partition("boot_a").is_err());
327     }
328 }
329