1use crate::BootloaderType;
6use anyhow::{Context as _, Error};
7use block_client::{BlockClient, MutableBufferSlice, RemoteBlockClient};
8use fidl::endpoints::Proxy;
9use fidl_fuchsia_mem::Buffer;
10use fidl_fuchsia_paver::{Asset, Configuration, DynamicDataSinkProxy};
11use fidl_fuchsia_storage_block::{BlockMarker, BlockProxy};
12
13use fuchsia_sync::Mutex;
14use futures::TryFutureExt;
15use futures::future::try_join;
16use payload_streamer::{BlockDevicePayloadStreamer, PayloadStreamer};
17use recovery_util_block::BlockDevice;
18use std::cmp::min;
19use std::fmt;
20
21const NS_PER_S: i64 = 1_000_000_000;
23
24#[derive(Debug, PartialEq)]
25pub enum PartitionPaveType {
26 Asset { r#type: Asset, config: Configuration },
27 Volume,
28 Bootloader,
29}
30
31pub struct Partition {
33 pave_type: PartitionPaveType,
34 src: String,
35 size: u64,
36 block_size: u64,
37}
38
39static WORKSTATION_INSTALLER_GPT: [u8; 16] = [
43 0xce, 0x98, 0xce, 0x4d, 0x7e, 0xe7, 0xc1, 0x45, 0xa8, 0x63, 0xca, 0xf9, 0x2f, 0x13, 0x30, 0xc1,
44];
45
46static WORKSTATION_PARTITION_GPTS: [[u8; 16]; 5] = [
50 [
51 0xfe, 0x94, 0xce, 0x5e, 0x86, 0x4c, 0xe8, 0x11, 0xa1, 0x5b, 0x48, 0x0f, 0xcf, 0x35, 0xf8,
52 0xe6,
53 ], [
55 0x6b, 0xe1, 0x09, 0xa4, 0xaa, 0x78, 0xcc, 0x4a, 0x5c, 0x99, 0x41, 0x1a, 0x62, 0x52, 0x23,
56 0x30,
57 ], [
59 0xf6, 0xff, 0x37, 0x9b, 0x58, 0x2e, 0x6a, 0x46, 0x3a, 0x98, 0xe0, 0x04, 0x0b, 0x6d, 0x92,
60 0xf7,
61 ], [
63 0xf6, 0xff, 0x37, 0x9b, 0x58, 0x2e, 0x6a, 0x46, 0x3a, 0x98, 0xe0, 0x04, 0x0b, 0x6d, 0x92,
64 0xf7,
65 ], [
67 0xf6, 0xff, 0x37, 0x9b, 0x58, 0x2e, 0x6a, 0x46, 0x3a, 0x98, 0xe0, 0x04, 0x0b, 0x6d, 0x92,
68 0xf7,
69 ], ];
71
72impl Partition {
73 async fn new(
82 src: String,
83 part: BlockProxy,
84 bootloader: BootloaderType,
85 ) -> Result<Option<Self>, Error> {
86 let (status, guid) = part.get_type_guid().await.context("Get type guid failed")?;
87 if let None = guid {
88 return Err(Error::new(zx::Status::from_raw(status)));
89 }
90
91 let (_status, name) = part.get_name().await.context("Get name failed")?;
92 let pave_type;
93 if let Some(string) = name {
94 let guid = guid.unwrap();
95 if guid.value != WORKSTATION_INSTALLER_GPT
96 && !(src.contains("usb-bus") && WORKSTATION_PARTITION_GPTS.contains(&guid.value))
97 {
98 return Ok(None);
99 }
100 if string == "storage-sparse" {
102 pave_type = Some(PartitionPaveType::Volume);
103 } else if bootloader == BootloaderType::Efi {
104 pave_type = Partition::get_efi_pave_type(&string.to_lowercase());
105 } else if bootloader == BootloaderType::Coreboot {
106 pave_type = Partition::get_coreboot_pave_type(&string);
107 } else {
108 pave_type = None;
109 }
110 } else {
111 return Ok(None);
112 }
113
114 if let Some(pave_type) = pave_type {
115 let info =
116 part.get_info().await.context("Get info failed")?.map_err(zx::Status::from_raw)?;
117 let block_size = info.block_size.into();
118 let size = info.block_count * block_size;
119
120 Ok(Some(Partition { pave_type, src, size, block_size }))
121 } else {
122 Ok(None)
123 }
124 }
125
126 fn get_efi_pave_type(label: &str) -> Option<PartitionPaveType> {
127 if label.starts_with("zircon_") && label.len() == "zircon_x".len() {
128 let configuration = Partition::letter_to_configuration(label.chars().last().unwrap());
129 Some(PartitionPaveType::Asset { r#type: Asset::Kernel, config: configuration })
130 } else if label.starts_with("vbmeta_") && label.len() == "vbmeta_x".len() {
131 let configuration = Partition::letter_to_configuration(label.chars().last().unwrap());
132 Some(PartitionPaveType::Asset {
133 r#type: Asset::VerifiedBootMetadata,
134 config: configuration,
135 })
136 } else if label.starts_with("efi")
137 || label.starts_with("fuchsia.esp")
138 || label.starts_with("bootloader")
139 {
140 Some(PartitionPaveType::Bootloader)
141 } else {
142 None
143 }
144 }
145
146 fn get_coreboot_pave_type(label: &str) -> Option<PartitionPaveType> {
147 if let Ok(re) = regex::Regex::new(r"^zircon_(.)\.signed$") {
148 if let Some(captures) = re.captures(label) {
149 let config = Partition::letter_to_configuration(
150 captures.get(1).unwrap().as_str().chars().last().unwrap(),
151 );
152 Some(PartitionPaveType::Asset { r#type: Asset::Kernel, config: config })
153 } else {
154 None
155 }
156 } else {
157 None
158 }
159 }
160
161 pub async fn get_partitions(
169 block_device: &BlockDevice,
170 all_devices: &Vec<BlockDevice>,
171 bootloader: BootloaderType,
172 ) -> Result<Vec<Self>, Error> {
173 let mut partitions = Vec::new();
174
175 for entry in all_devices {
176 if !entry.topo_path.starts_with(&block_device.topo_path) || entry == block_device {
177 continue;
180 }
181 let (local, remote) = zx::Channel::create();
182 fdio::service_connect(&entry.class_path, remote).context("Connecting to partition")?;
183 let local = fidl::AsyncChannel::from_channel(local);
184
185 let proxy = BlockProxy::from_channel(local);
186 if let Some(partition) = Partition::new(entry.class_path.clone(), proxy, bootloader)
187 .await
188 .context(format!(
189 "Creating partition for block device at {} ({})",
190 entry.topo_path, entry.class_path
191 ))?
192 {
193 partitions.push(partition);
194 }
195 }
196 Ok(partitions)
197 }
198
199 pub async fn pave<F>(
201 &self,
202 data_sink: &DynamicDataSinkProxy,
203 progress_callback: &F,
204 ) -> Result<(), Error>
205 where
206 F: Send + Sync + Fn(usize, usize) -> (),
207 {
208 match self.pave_type {
209 PartitionPaveType::Asset { r#type: asset, config } => {
210 let fidl_buf = self.read_data().await?;
211 data_sink.write_asset(config, asset, fidl_buf).await?;
212 }
213 PartitionPaveType::Bootloader => {
214 let fidl_buf = self.read_data().await?;
215 data_sink.write_firmware(Configuration::A, "", fidl_buf).await?;
217 }
218 PartitionPaveType::Volume => {
219 self.pave_volume(data_sink, progress_callback).await?;
220 }
221 };
222 Ok(())
223 }
224
225 async fn pave_volume<F>(
226 &self,
227 data_sink: &DynamicDataSinkProxy,
228 progress_callback: &F,
229 ) -> Result<(), Error>
230 where
231 F: Send + Sync + Fn(usize, usize) -> (),
232 {
233 let partition_block =
235 fuchsia_component::client::connect_to_protocol_at_path::<BlockMarker>(&self.src)?;
236 let streamer: Box<dyn PayloadStreamer> =
237 Box::new(BlockDevicePayloadStreamer::new(partition_block).await?);
238 let start_time = zx::MonotonicInstant::get();
239 let last_percent = Mutex::new(0 as i64);
240 let status_callback = move |data_read, data_total| {
241 progress_callback(data_read, data_total);
242 if data_total == 0 {
243 return;
244 }
245 let percent: i64 =
246 unsafe { (((data_read as f64) / (data_total as f64)) * 100.0).to_int_unchecked() };
247 let mut prev = last_percent.lock();
248 if percent != *prev {
249 let now = zx::MonotonicInstant::get();
250 let nanos = now.into_nanos() - start_time.into_nanos();
251 let secs = nanos / NS_PER_S;
252 let rate = ((data_read as f64) / (secs as f64)) / (1024 as f64);
253
254 log::info!("Paving FVM: {}% ({:.02} KiB/s)", percent, rate);
255 *prev = percent;
256 }
257 };
258 let (client, server) =
259 fidl::endpoints::create_request_stream::<fidl_fuchsia_paver::PayloadStreamMarker>();
260
261 try_join(
263 streamer.service_payload_stream_requests(server, Some(&status_callback)),
264 data_sink.write_volumes(client).map_err(|e| e.into()),
265 )
266 .await?;
267
268 Ok(())
269 }
270
271 pub async fn pave_b(&self, data_sink: &DynamicDataSinkProxy) -> Result<(), Error> {
274 if !self.is_ab() {
275 return Err(Error::from(zx::Status::NOT_SUPPORTED));
276 }
277
278 let fidl_buf = self.read_data().await?;
279 match self.pave_type {
280 PartitionPaveType::Asset { r#type: asset, config: _ } => {
281 data_sink.write_asset(Configuration::B, asset, fidl_buf).await?;
285 Ok(())
286 }
287 _ => Err(Error::from(zx::Status::NOT_SUPPORTED)),
288 }
289 }
290
291 pub fn is_ab(&self) -> bool {
293 if let PartitionPaveType::Asset { r#type: _, config } = self.pave_type {
294 return config == Configuration::A;
297 }
298 return false;
299 }
300
301 async fn read_data(&self) -> Result<Buffer, Error> {
303 let mut rounded_size = self.size;
304 let page_size = u64::from(zx::system_get_page_size());
305 if rounded_size % page_size != 0 {
306 rounded_size += page_size;
307 rounded_size -= rounded_size % page_size;
308 }
309
310 let vmo = zx::Vmo::create_with_opts(zx::VmoOptions::RESIZABLE, rounded_size)?;
311
312 let proxy =
313 fuchsia_component::client::connect_to_protocol_at_path::<BlockMarker>(&self.src)
314 .with_context(|| format!("Connecting to block device {}", &self.src))?;
315 let block_device = RemoteBlockClient::new(proxy).await?;
316 let vmo_id = block_device.attach_vmo(&vmo).await?;
317
318 let max_read_length: u64 = self.block_size * 100;
320 let mut read: u64 = 0;
321 while read < self.size {
322 let read_size = min(self.size - read, max_read_length);
323 if let Err(e) = block_device
324 .read_at(MutableBufferSlice::new_with_vmo_id(&vmo_id, read, read_size), read)
325 .await
326 .context("Reading from partition to VMO")
327 {
328 block_device.detach_vmo(vmo_id).await?;
330 return Err(e);
331 }
332
333 read += read_size;
334 }
335
336 block_device.detach_vmo(vmo_id).await?;
337
338 return Ok(Buffer { vmo: fidl::Vmo::from(vmo), size: self.size });
339 }
340
341 fn letter_to_configuration(letter: char) -> Configuration {
345 match letter {
348 'A' | 'a' => Configuration::A,
349 'B' | 'b' => Configuration::A,
350 'R' | 'r' => Configuration::Recovery,
351 _ => Configuration::A,
352 }
353 }
354}
355
356impl fmt::Debug for Partition {
357 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
358 match self.pave_type {
359 PartitionPaveType::Asset { r#type, config } => write!(
360 f,
361 "Partition[src={}, pave_type={:?}, asset={:?}, config={:?}]",
362 self.src, self.pave_type, r#type, config
363 ),
364 _ => write!(f, "Partition[src={}, pave_type={:?}]", self.src, self.pave_type),
365 }
366 }
367}
368
369#[cfg(test)]
370mod tests {
371 use super::*;
372 use fidl_fuchsia_storage_block::{
373 BlockInfo, BlockMarker, BlockRequest, BlockRequestStream, DeviceFlag, Guid,
374 };
375 use fuchsia_async as fasync;
376 use futures::TryStreamExt;
377
378 async fn serve_partition(
379 label: &str,
380 block_size: u32,
381 block_count: u64,
382 guid: [u8; 16],
383 mut stream: BlockRequestStream,
384 ) -> Result<(), Error> {
385 while let Some(req) = stream.try_next().await? {
386 match req {
387 BlockRequest::GetName { responder } => responder.send(0, Some(label))?,
388 BlockRequest::GetInfo { responder } => responder.send(Ok(&BlockInfo {
389 block_count,
390 block_size,
391 max_transfer_size: 0,
392 flags: DeviceFlag::empty(),
393 }))?,
394 BlockRequest::GetTypeGuid { responder } => {
395 responder.send(0, Some(&Guid { value: guid }))?
396 }
397 _ => panic!("Expected a GetInfo/GetName request, but did not get one."),
398 }
399 }
400 Ok(())
401 }
402
403 fn mock_partition(
404 label: &'static str,
405 block_size: usize,
406 block_count: usize,
407 guid: [u8; 16],
408 ) -> Result<BlockProxy, Error> {
409 let (proxy, stream) = fidl::endpoints::create_proxy_and_stream::<BlockMarker>();
410 fasync::Task::local(
411 serve_partition(
412 label,
413 block_size.try_into().unwrap(),
414 block_count.try_into().unwrap(),
415 guid,
416 stream,
417 )
418 .unwrap_or_else(|e| panic!("Error while serving fake block device: {}", e)),
419 )
420 .detach();
421 Ok(proxy)
422 }
423
424 #[fasync::run_singlethreaded(test)]
425 async fn test_new_partition_bad_guid() -> Result<(), Error> {
426 let proxy = mock_partition("zircon_a", 512, 1000, [0xaa; 16])?;
427 let part = Partition::new("zircon_a".to_string(), proxy, BootloaderType::Efi).await?;
428 assert!(part.is_none());
429 Ok(())
430 }
431
432 #[fasync::run_singlethreaded(test)]
433 async fn test_new_partition_zircona() -> Result<(), Error> {
434 let proxy = mock_partition("zircon_a", 512, 1000, WORKSTATION_INSTALLER_GPT)?;
435 let part = Partition::new("zircon_a".to_string(), proxy, BootloaderType::Efi).await?;
436 assert!(part.is_some());
437 let part = part.unwrap();
438 assert_eq!(
439 part.pave_type,
440 PartitionPaveType::Asset { r#type: Asset::Kernel, config: Configuration::A }
441 );
442 assert_eq!(part.size, 512 * 1000);
443 assert_eq!(part.src, "zircon_a");
444 assert!(part.is_ab());
445 Ok(())
446 }
447
448 #[fasync::run_singlethreaded(test)]
449 async fn test_new_partition_zirconb() -> Result<(), Error> {
450 let proxy = mock_partition("zircon_b", 20, 1000, WORKSTATION_INSTALLER_GPT)?;
451 let part = Partition::new("zircon_b".to_string(), proxy, BootloaderType::Efi).await?;
452 assert!(part.is_some());
453 let part = part.unwrap();
454 assert_eq!(
455 part.pave_type,
456 PartitionPaveType::Asset { r#type: Asset::Kernel, config: Configuration::A }
457 );
458 assert_eq!(part.size, 20 * 1000);
459 assert_eq!(part.src, "zircon_b");
460 assert!(part.is_ab());
461 Ok(())
462 }
463
464 #[fasync::run_singlethreaded(test)]
465 async fn test_new_partition_zirconr() -> Result<(), Error> {
466 let proxy = mock_partition("zircon_r", 40, 200, WORKSTATION_INSTALLER_GPT)?;
467 let part = Partition::new("zircon_r".to_string(), proxy, BootloaderType::Efi).await?;
468 assert!(part.is_some());
469 let part = part.unwrap();
470 assert_eq!(
471 part.pave_type,
472 PartitionPaveType::Asset { r#type: Asset::Kernel, config: Configuration::Recovery }
473 );
474 assert_eq!(part.size, 40 * 200);
475 assert_eq!(part.src, "zircon_r");
476 assert!(!part.is_ab());
477 Ok(())
478 }
479
480 async fn new_partition_vbmetax_test_helper(
481 name: &'static str,
482 expected_config: Configuration,
483 ) -> Result<(), Error> {
484 let proxy = mock_partition(name, 40, 200, WORKSTATION_INSTALLER_GPT)?;
485 let part = Partition::new(name.to_string(), proxy, BootloaderType::Efi).await?;
486 assert!(part.is_some());
487 let part = part.unwrap();
488 assert_eq!(
489 part.pave_type,
490 PartitionPaveType::Asset {
491 r#type: Asset::VerifiedBootMetadata,
492 config: expected_config
493 }
494 );
495 assert_eq!(part.size, 40 * 200);
496 assert_eq!(part.src, name);
497 Ok(())
498 }
499
500 #[fasync::run_singlethreaded(test)]
501 async fn test_new_partition_vbmetaa() -> Result<(), Error> {
502 new_partition_vbmetax_test_helper("vbmeta_a", Configuration::A).await
503 }
504
505 #[fasync::run_singlethreaded(test)]
506 async fn test_new_partition_vbmetab() -> Result<(), Error> {
507 new_partition_vbmetax_test_helper("vbmeta_b", Configuration::A).await
510 }
511
512 #[fasync::run_singlethreaded(test)]
513 async fn test_new_partition_vbmetar() -> Result<(), Error> {
514 new_partition_vbmetax_test_helper("vbmeta_r", Configuration::Recovery).await
515 }
516
517 #[fasync::run_singlethreaded(test)]
518 async fn test_new_partition_efi() -> Result<(), Error> {
519 let proxy = mock_partition("efi", 512, 1000, WORKSTATION_INSTALLER_GPT)?;
520 let part = Partition::new("efi".to_string(), proxy, BootloaderType::Efi).await?;
521 assert!(part.is_some());
522 let part = part.unwrap();
523 assert_eq!(part.pave_type, PartitionPaveType::Bootloader);
524 assert_eq!(part.size, 512 * 1000);
525 assert_eq!(part.src, "efi");
526 assert!(!part.is_ab());
527 Ok(())
528 }
529
530 #[fasync::run_singlethreaded(test)]
531 async fn test_new_partition_fvm() -> Result<(), Error> {
532 let proxy = mock_partition("storage-sparse", 2048, 4097, WORKSTATION_INSTALLER_GPT)?;
533 let part = Partition::new("storage-sparse".to_string(), proxy, BootloaderType::Efi).await?;
534 assert!(part.is_some());
535 let part = part.unwrap();
536 assert_eq!(part.pave_type, PartitionPaveType::Volume);
537 assert_eq!(part.size, 2048 * 4097);
538 assert_eq!(part.src, "storage-sparse");
539 assert!(!part.is_ab());
540 Ok(())
541 }
542
543 #[fasync::run_singlethreaded(test)]
544 async fn test_zircona_unsigned_coreboot() -> Result<(), Error> {
545 let proxy = mock_partition("zircon_a", 512, 1000, WORKSTATION_INSTALLER_GPT)?;
546 let part = Partition::new("zircon_a".to_string(), proxy, BootloaderType::Coreboot).await?;
547 assert!(part.is_none());
548 Ok(())
549 }
550
551 #[fasync::run_singlethreaded(test)]
552 async fn test_zircona_signed_coreboot() -> Result<(), Error> {
553 let proxy = mock_partition("zircon_a.signed", 512, 1000, WORKSTATION_INSTALLER_GPT)?;
554 let part =
555 Partition::new("zircon_a.signed".to_string(), proxy, BootloaderType::Coreboot).await?;
556 assert!(part.is_some());
557 let part = part.unwrap();
558 assert_eq!(
559 part.pave_type,
560 PartitionPaveType::Asset { r#type: Asset::Kernel, config: Configuration::A }
561 );
562 assert_eq!(part.size, 512 * 1000);
563 assert_eq!(part.src, "zircon_a.signed");
564 assert!(part.is_ab());
565 Ok(())
566 }
567
568 #[fasync::run_singlethreaded(test)]
569 async fn test_new_partition_unknown() -> Result<(), Error> {
570 let proxy = mock_partition("unknown-label", 512, 1000, WORKSTATION_INSTALLER_GPT)?;
571 let part = Partition::new("unknown-label".to_string(), proxy, BootloaderType::Efi).await?;
572 assert!(part.is_none());
573 Ok(())
574 }
575
576 #[fasync::run_singlethreaded(test)]
577 async fn test_new_partition_zedboot_efi() -> Result<(), Error> {
578 let proxy = mock_partition("zedboot-efi", 512, 1000, WORKSTATION_INSTALLER_GPT)?;
579 let part = Partition::new("zedboot-efi".to_string(), proxy, BootloaderType::Efi).await?;
580 assert!(part.is_none());
581 Ok(())
582 }
583
584 #[fasync::run_singlethreaded(test)]
585 async fn test_invalid_partitions_coreboot() -> Result<(), Error> {
586 let proxy = mock_partition("zircon_.signed", 512, 1000, WORKSTATION_INSTALLER_GPT)?;
587 let part =
588 Partition::new("zircon_.signed".to_string(), proxy, BootloaderType::Coreboot).await?;
589 assert!(part.is_none());
590
591 let proxy = mock_partition("zircon_aa.signed", 512, 1000, WORKSTATION_INSTALLER_GPT)?;
592 let part =
593 Partition::new("zircon_aa.signed".to_string(), proxy, BootloaderType::Coreboot).await?;
594 assert!(part.is_none());
595
596 Ok(())
597 }
598
599 #[fasync::run_singlethreaded(test)]
600 async fn test_invalid_partitions_efi() -> Result<(), Error> {
601 let proxy = mock_partition("zircon_", 512, 1000, WORKSTATION_INSTALLER_GPT)?;
602 let part = Partition::new("zircon_".to_string(), proxy, BootloaderType::Efi).await?;
603 assert!(part.is_none());
604
605 let proxy = mock_partition("zircon_aa", 512, 1000, WORKSTATION_INSTALLER_GPT)?;
606 let part = Partition::new("zircon_aa".to_string(), proxy, BootloaderType::Efi).await?;
607 assert!(part.is_none());
608
609 let proxy = mock_partition("zircon_a.signed", 512, 1000, WORKSTATION_INSTALLER_GPT)?;
610 let part =
611 Partition::new("zircon_a.signed".to_string(), proxy, BootloaderType::Efi).await?;
612 assert!(part.is_none());
613 Ok(())
614 }
615
616 #[fasync::run_singlethreaded(test)]
617 async fn test_new_partition_usb_bad_guid() -> Result<(), Error> {
618 let proxy = mock_partition("zircon_a", 512, 1000, [0xaa; 16])?;
619 let part = Partition::new("/dev/usb-bus".to_string(), proxy, BootloaderType::Efi).await?;
620 assert!(part.is_none());
621 Ok(())
622 }
623
624 #[fasync::run_singlethreaded(test)]
625 async fn test_new_partition_usb_zircona() -> Result<(), Error> {
626 let proxy = mock_partition("zircon_a", 512, 1000, WORKSTATION_PARTITION_GPTS[2])?;
627 let part = Partition::new("/dev/usb-bus".to_string(), proxy, BootloaderType::Efi).await?;
628 assert!(part.is_some());
629 let part = part.unwrap();
630 assert_eq!(
631 part.pave_type,
632 PartitionPaveType::Asset { r#type: Asset::Kernel, config: Configuration::A }
633 );
634 assert_eq!(part.size, 512 * 1000);
635 assert_eq!(part.src, "/dev/usb-bus");
636 assert!(part.is_ab());
637 Ok(())
638 }
639
640 #[fasync::run_singlethreaded(test)]
641 async fn test_new_partition_usb_zirconb() -> Result<(), Error> {
642 let proxy = mock_partition("zircon_b", 20, 1000, WORKSTATION_PARTITION_GPTS[3])?;
643 let part = Partition::new("/dev/usb-bus".to_string(), proxy, BootloaderType::Efi).await?;
644 assert!(part.is_some());
645 let part = part.unwrap();
646 assert_eq!(
647 part.pave_type,
648 PartitionPaveType::Asset { r#type: Asset::Kernel, config: Configuration::A }
649 );
650 assert_eq!(part.size, 20 * 1000);
651 assert_eq!(part.src, "/dev/usb-bus");
652 assert!(part.is_ab());
653 Ok(())
654 }
655
656 #[fasync::run_singlethreaded(test)]
657 async fn test_new_partition_usb_zirconr() -> Result<(), Error> {
658 let proxy = mock_partition("zircon_r", 40, 200, WORKSTATION_PARTITION_GPTS[4])?;
659 let part = Partition::new("/dev/usb-bus".to_string(), proxy, BootloaderType::Efi).await?;
660 assert!(part.is_some());
661 let part = part.unwrap();
662 assert_eq!(
663 part.pave_type,
664 PartitionPaveType::Asset { r#type: Asset::Kernel, config: Configuration::Recovery }
665 );
666 assert_eq!(part.size, 40 * 200);
667 assert_eq!(part.src, "/dev/usb-bus");
668 assert!(!part.is_ab());
669 Ok(())
670 }
671
672 #[fasync::run_singlethreaded(test)]
673 async fn test_new_partition_usb_efi() -> Result<(), Error> {
674 let proxy = mock_partition("efi-system", 512, 1000, WORKSTATION_PARTITION_GPTS[0])?;
675 let part = Partition::new("/dev/usb-bus".to_string(), proxy, BootloaderType::Efi).await?;
676 assert!(part.is_some());
677 let part = part.unwrap();
678 assert_eq!(part.pave_type, PartitionPaveType::Bootloader);
679 assert_eq!(part.size, 512 * 1000);
680 assert_eq!(part.src, "/dev/usb-bus");
681 assert!(!part.is_ab());
682 Ok(())
683 }
684}