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