Skip to main content

installer/
partition.rs

1// Copyright 2022 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use 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 recovery_util_block::BlockDevice;
14use std::cmp::min;
15use std::fmt;
16
17#[derive(Debug, PartialEq)]
18pub enum PartitionPaveType {
19    Asset { r#type: Asset, config: Configuration },
20    Volume,
21    Bootloader,
22}
23
24/// Represents a partition that will be paved to the disk.
25pub struct Partition {
26    pave_type: PartitionPaveType,
27    src: String,
28    size: u64,
29    block_size: u64,
30}
31
32/// This GUID is used by the installer to identify partitions that contain
33/// data that will be installed to disk. The `fx mkinstaller` tool generates
34/// images containing partitions with this GUID.
35static WORKSTATION_INSTALLER_GPT: [u8; 16] = [
36    0xce, 0x98, 0xce, 0x4d, 0x7e, 0xe7, 0xc1, 0x45, 0xa8, 0x63, 0xca, 0xf9, 0x2f, 0x13, 0x30, 0xc1,
37];
38
39/// These GUIDs are used by the installer to identify partitions that contain
40/// data that will be installed to disk from a usb disk. The `fx make-fuchsia-vol`
41/// tool generates images containing partitions with these GUIDs.
42static WORKSTATION_PARTITION_GPTS: [[u8; 16]; 5] = [
43    [
44        0xfe, 0x94, 0xce, 0x5e, 0x86, 0x4c, 0xe8, 0x11, 0xa1, 0x5b, 0x48, 0x0f, 0xcf, 0x35, 0xf8,
45        0xe6,
46    ], // bootloader
47    [
48        0x6b, 0xe1, 0x09, 0xa4, 0xaa, 0x78, 0xcc, 0x4a, 0x5c, 0x99, 0x41, 0x1a, 0x62, 0x52, 0x23,
49        0x30,
50    ], // durable_boot
51    [
52        0xf6, 0xff, 0x37, 0x9b, 0x58, 0x2e, 0x6a, 0x46, 0x3a, 0x98, 0xe0, 0x04, 0x0b, 0x6d, 0x92,
53        0xf7,
54    ], // zircon_a
55    [
56        0xf6, 0xff, 0x37, 0x9b, 0x58, 0x2e, 0x6a, 0x46, 0x3a, 0x98, 0xe0, 0x04, 0x0b, 0x6d, 0x92,
57        0xf7,
58    ], // zircon_b
59    [
60        0xf6, 0xff, 0x37, 0x9b, 0x58, 0x2e, 0x6a, 0x46, 0x3a, 0x98, 0xe0, 0x04, 0x0b, 0x6d, 0x92,
61        0xf7,
62    ], // zircon_r
63];
64
65impl Partition {
66    /// Creates a new partition. Returns `None` if the partition is not
67    /// a partition that should be paved to the disk.
68    ///
69    /// # Arguments
70    /// * `src` - path to a block device that represents this partition.
71    /// * `part` - a |BlockProxy| that is connected to this partition.
72    /// * `bootloader` - the |BootloaderType| of this device.
73    ///
74    async fn new(
75        src: String,
76        part: BlockProxy,
77        bootloader: BootloaderType,
78    ) -> Result<Option<Self>, Error> {
79        let (status, guid) = part.get_type_guid().await.context("Get type guid failed")?;
80        if let None = guid {
81            return Err(Error::new(zx::Status::err_from_raw(status)));
82        }
83
84        let (_status, name) = part.get_name().await.context("Get name failed")?;
85        let pave_type;
86        if let Some(string) = name {
87            let guid = guid.unwrap();
88            if guid.value != WORKSTATION_INSTALLER_GPT
89                && !(src.contains("usb-bus") && WORKSTATION_PARTITION_GPTS.contains(&guid.value))
90            {
91                return Ok(None);
92            }
93            // TODO(https://fxbug.dev/42121026) support any other partitions that might be needed
94            if string == "storage-sparse" {
95                pave_type = Some(PartitionPaveType::Volume);
96            } else if bootloader == BootloaderType::Efi {
97                pave_type = Partition::get_efi_pave_type(&string.to_lowercase());
98            } else if bootloader == BootloaderType::Coreboot {
99                pave_type = Partition::get_coreboot_pave_type(&string);
100            } else {
101                pave_type = None;
102            }
103        } else {
104            return Ok(None);
105        }
106
107        if let Some(pave_type) = pave_type {
108            let info = part
109                .get_info()
110                .await
111                .context("Get info failed")?
112                .map_err(zx::Status::err_from_raw)?;
113            let block_size = info.block_size.into();
114            let size = info.block_count * block_size;
115
116            Ok(Some(Partition { pave_type, src, size, block_size }))
117        } else {
118            Ok(None)
119        }
120    }
121
122    fn get_efi_pave_type(label: &str) -> Option<PartitionPaveType> {
123        if label.starts_with("zircon_") && label.len() == "zircon_x".len() {
124            let configuration = Partition::letter_to_configuration(label.chars().last().unwrap());
125            Some(PartitionPaveType::Asset { r#type: Asset::Kernel, config: configuration })
126        } else if label.starts_with("vbmeta_") && label.len() == "vbmeta_x".len() {
127            let configuration = Partition::letter_to_configuration(label.chars().last().unwrap());
128            Some(PartitionPaveType::Asset {
129                r#type: Asset::VerifiedBootMetadata,
130                config: configuration,
131            })
132        } else if label.starts_with("efi")
133            || label.starts_with("fuchsia.esp")
134            || label.starts_with("bootloader")
135        {
136            Some(PartitionPaveType::Bootloader)
137        } else {
138            None
139        }
140    }
141
142    fn get_coreboot_pave_type(label: &str) -> Option<PartitionPaveType> {
143        if let Ok(re) = regex_lite::Regex::new(r"^zircon_(.)\.signed$") {
144            if let Some(captures) = re.captures(label) {
145                let config = Partition::letter_to_configuration(
146                    captures.get(1).unwrap().as_str().chars().last().unwrap(),
147                );
148                Some(PartitionPaveType::Asset { r#type: Asset::Kernel, config: config })
149            } else {
150                None
151            }
152        } else {
153            None
154        }
155    }
156
157    /// Gather all partitions that are children of the given block device,
158    /// and return them.
159    ///
160    /// # Arguments
161    /// * `block_device` - the |BlockDevice| to get partitions from.
162    /// * `all_devices` - All known block devices in the system.
163    /// * `bootloader` - the |BootloaderType| of this device.
164    pub async fn get_partitions(
165        block_device: &BlockDevice,
166        all_devices: &Vec<BlockDevice>,
167        bootloader: BootloaderType,
168    ) -> Result<Vec<Self>, Error> {
169        let mut partitions = Vec::new();
170
171        for entry in all_devices {
172            if !entry.topo_path.starts_with(&block_device.topo_path) || entry == block_device {
173                // Skip partitions that are not children of this block device, and skip the block
174                // device itself.
175                continue;
176            }
177            let (local, remote) = zx::Channel::create();
178            fdio::service_connect(&entry.class_path, remote).context("Connecting to partition")?;
179            let local = fidl::AsyncChannel::from_channel(local);
180
181            let proxy = BlockProxy::from_channel(local);
182            if let Some(partition) = Partition::new(entry.class_path.clone(), proxy, bootloader)
183                .await
184                .context(format!(
185                    "Creating partition for block device at {} ({})",
186                    entry.topo_path, entry.class_path
187                ))?
188            {
189                partitions.push(partition);
190            }
191        }
192        Ok(partitions)
193    }
194
195    /// Pave this partition to disk, using the given |DynamicDataSinkProxy|.
196    pub async fn pave<F>(
197        &self,
198        data_sink: &DynamicDataSinkProxy,
199        progress_callback: &F,
200    ) -> Result<(), Error>
201    where
202        F: Send + Sync + Fn(usize, usize) -> (),
203    {
204        match self.pave_type {
205            PartitionPaveType::Asset { r#type: asset, config } => {
206                let fidl_buf = self.read_data().await?;
207                data_sink.write_asset(config, asset, fidl_buf).await?;
208            }
209            PartitionPaveType::Bootloader => {
210                let fidl_buf = self.read_data().await?;
211                // Currently we only store the bootloader in slot A, we don't use an A/B/R scheme.
212                data_sink.write_firmware(Configuration::A, "", fidl_buf).await?;
213            }
214            PartitionPaveType::Volume => {
215                self.pave_volume(data_sink, progress_callback).await?;
216            }
217        };
218        Ok(())
219    }
220
221    async fn pave_volume<F>(
222        &self,
223        _data_sink: &DynamicDataSinkProxy,
224        _progress_callback: &F,
225    ) -> Result<(), Error>
226    where
227        F: Send + Sync + Fn(usize, usize) -> (),
228    {
229        Err(Error::from(zx::Status::NOT_SUPPORTED))
230    }
231
232    /// Pave this A/B partition to its 'B' slot.
233    /// Will return an error if the partition is not an A/B partition.
234    pub async fn pave_b(&self, data_sink: &DynamicDataSinkProxy) -> Result<(), Error> {
235        if !self.is_ab() {
236            return Err(Error::from(zx::Status::NOT_SUPPORTED));
237        }
238
239        let fidl_buf = self.read_data().await?;
240        match self.pave_type {
241            PartitionPaveType::Asset { r#type: asset, config: _ } => {
242                // pave() will always pave to A, so this always paves to B.
243                // The A/B config from the partition is not respected because on a fresh
244                // install we want A/B to be identical, so we install the same thing to both.
245                data_sink.write_asset(Configuration::B, asset, fidl_buf).await?;
246                Ok(())
247            }
248            _ => Err(Error::from(zx::Status::NOT_SUPPORTED)),
249        }
250    }
251
252    /// Returns true if this partition has A/B variants when installed.
253    pub fn is_ab(&self) -> bool {
254        if let PartitionPaveType::Asset { r#type: _, config } = self.pave_type {
255            // We only check against the A configuration because |letter_to_configuration|
256            // returns A for 'A' and 'B' configurations.
257            return config == Configuration::A;
258        }
259        return false;
260    }
261
262    /// Read this partition into a FIDL buffer.
263    async fn read_data(&self) -> Result<Buffer, Error> {
264        let mut rounded_size = self.size;
265        let page_size = u64::from(zx::system_get_page_size());
266        if rounded_size % page_size != 0 {
267            rounded_size += page_size;
268            rounded_size -= rounded_size % page_size;
269        }
270
271        let vmo = zx::Vmo::create_with_opts(zx::VmoOptions::RESIZABLE, rounded_size)?;
272
273        let proxy =
274            fuchsia_component::client::connect_to_protocol_at_path::<BlockMarker>(&self.src)
275                .with_context(|| format!("Connecting to block device {}", self.src))?;
276        let block_device = RemoteBlockClient::new(proxy).await?;
277        // SAFETY: We only attach this VMO once, and we do not hold any Rust references to its
278        // memory (in fact, we do not even map it).
279        let vmo_id = unsafe { block_device.attach_vmo(&vmo) }.await?;
280
281        // Reading too much at a time causes the UMS driver to return an error.
282        let max_read_length: u64 = self.block_size * 100;
283        let mut read: u64 = 0;
284        while read < self.size {
285            let read_size = min(self.size - read, max_read_length);
286            if let Err(e) = block_device
287                .read_at(MutableBufferSlice::new_with_vmo_id(&vmo_id, read, read_size), read)
288                .await
289                .context("Reading from partition to VMO")
290            {
291                // Need to detach before returning.
292                block_device.detach_vmo(vmo_id).await?;
293                return Err(e);
294            }
295
296            read += read_size;
297        }
298
299        block_device.detach_vmo(vmo_id).await?;
300
301        return Ok(Buffer { vmo: fidl::Vmo::from(vmo), size: self.size });
302    }
303
304    /// Return the |Configuration| that is represented by the given
305    /// character. Returns 'Recovery' for the letters 'R' and 'r', and 'A' for
306    /// anything else.
307    fn letter_to_configuration(letter: char) -> Configuration {
308        // Note that we treat 'A' and 'B' the same, as the installer will install
309        // the same image to both A and B.
310        match letter {
311            'A' | 'a' => Configuration::A,
312            'B' | 'b' => Configuration::A,
313            'R' | 'r' => Configuration::Recovery,
314            _ => Configuration::A,
315        }
316    }
317}
318
319impl fmt::Debug for Partition {
320    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
321        match self.pave_type {
322            PartitionPaveType::Asset { r#type, config } => write!(
323                f,
324                "Partition[src={}, pave_type={:?}, asset={:?}, config={:?}]",
325                self.src, self.pave_type, r#type, config
326            ),
327            _ => write!(f, "Partition[src={}, pave_type={:?}]", self.src, self.pave_type),
328        }
329    }
330}
331
332#[cfg(test)]
333mod tests {
334    use super::*;
335    use fidl_fuchsia_storage_block::{
336        BlockInfo, BlockMarker, BlockRequest, BlockRequestStream, DeviceFlag, Guid,
337    };
338    use fuchsia_async as fasync;
339    use futures::{TryFutureExt, TryStreamExt};
340
341    async fn serve_partition(
342        label: &str,
343        block_size: u32,
344        block_count: u64,
345        guid: [u8; 16],
346        mut stream: BlockRequestStream,
347    ) -> Result<(), Error> {
348        while let Some(req) = stream.try_next().await? {
349            match req {
350                BlockRequest::GetName { responder } => responder.send(0, Some(label))?,
351                BlockRequest::GetInfo { responder } => responder.send(Ok(&BlockInfo {
352                    block_count,
353                    block_size,
354                    max_transfer_size: 0,
355                    flags: DeviceFlag::empty(),
356                }))?,
357                BlockRequest::GetTypeGuid { responder } => {
358                    responder.send(0, Some(&Guid { value: guid }))?
359                }
360                _ => panic!("Expected a GetInfo/GetName request, but did not get one."),
361            }
362        }
363        Ok(())
364    }
365
366    fn mock_partition(
367        label: &'static str,
368        block_size: usize,
369        block_count: usize,
370        guid: [u8; 16],
371    ) -> Result<BlockProxy, Error> {
372        let (proxy, stream) = fidl::endpoints::create_proxy_and_stream::<BlockMarker>();
373        fasync::Task::local(
374            serve_partition(
375                label,
376                block_size.try_into().unwrap(),
377                block_count.try_into().unwrap(),
378                guid,
379                stream,
380            )
381            .unwrap_or_else(|e| panic!("Error while serving fake block device: {}", e)),
382        )
383        .detach();
384        Ok(proxy)
385    }
386
387    #[fuchsia::test]
388    async fn test_new_partition_bad_guid() -> Result<(), Error> {
389        let proxy = mock_partition("zircon_a", 512, 1000, [0xaa; 16])?;
390        let part = Partition::new("zircon_a".to_string(), proxy, BootloaderType::Efi).await?;
391        assert!(part.is_none());
392        Ok(())
393    }
394
395    #[fuchsia::test]
396    async fn test_new_partition_zircona() -> Result<(), Error> {
397        let proxy = mock_partition("zircon_a", 512, 1000, WORKSTATION_INSTALLER_GPT)?;
398        let part = Partition::new("zircon_a".to_string(), proxy, BootloaderType::Efi).await?;
399        assert!(part.is_some());
400        let part = part.unwrap();
401        assert_eq!(
402            part.pave_type,
403            PartitionPaveType::Asset { r#type: Asset::Kernel, config: Configuration::A }
404        );
405        assert_eq!(part.size, 512 * 1000);
406        assert_eq!(part.src, "zircon_a");
407        assert!(part.is_ab());
408        Ok(())
409    }
410
411    #[fuchsia::test]
412    async fn test_new_partition_zirconb() -> Result<(), Error> {
413        let proxy = mock_partition("zircon_b", 20, 1000, WORKSTATION_INSTALLER_GPT)?;
414        let part = Partition::new("zircon_b".to_string(), proxy, BootloaderType::Efi).await?;
415        assert!(part.is_some());
416        let part = part.unwrap();
417        assert_eq!(
418            part.pave_type,
419            PartitionPaveType::Asset { r#type: Asset::Kernel, config: Configuration::A }
420        );
421        assert_eq!(part.size, 20 * 1000);
422        assert_eq!(part.src, "zircon_b");
423        assert!(part.is_ab());
424        Ok(())
425    }
426
427    #[fuchsia::test]
428    async fn test_new_partition_zirconr() -> Result<(), Error> {
429        let proxy = mock_partition("zircon_r", 40, 200, WORKSTATION_INSTALLER_GPT)?;
430        let part = Partition::new("zircon_r".to_string(), proxy, BootloaderType::Efi).await?;
431        assert!(part.is_some());
432        let part = part.unwrap();
433        assert_eq!(
434            part.pave_type,
435            PartitionPaveType::Asset { r#type: Asset::Kernel, config: Configuration::Recovery }
436        );
437        assert_eq!(part.size, 40 * 200);
438        assert_eq!(part.src, "zircon_r");
439        assert!(!part.is_ab());
440        Ok(())
441    }
442
443    async fn new_partition_vbmetax_test_helper(
444        name: &'static str,
445        expected_config: Configuration,
446    ) -> Result<(), Error> {
447        let proxy = mock_partition(name, 40, 200, WORKSTATION_INSTALLER_GPT)?;
448        let part = Partition::new(name.to_string(), proxy, BootloaderType::Efi).await?;
449        assert!(part.is_some());
450        let part = part.unwrap();
451        assert_eq!(
452            part.pave_type,
453            PartitionPaveType::Asset {
454                r#type: Asset::VerifiedBootMetadata,
455                config: expected_config
456            }
457        );
458        assert_eq!(part.size, 40 * 200);
459        assert_eq!(part.src, name);
460        Ok(())
461    }
462
463    #[fuchsia::test]
464    async fn test_new_partition_vbmetaa() -> Result<(), Error> {
465        new_partition_vbmetax_test_helper("vbmeta_a", Configuration::A).await
466    }
467
468    #[fuchsia::test]
469    async fn test_new_partition_vbmetab() -> Result<(), Error> {
470        // 'A' and 'B' are treated the same, as the installer will install
471        // the same image to both A and B.
472        new_partition_vbmetax_test_helper("vbmeta_b", Configuration::A).await
473    }
474
475    #[fuchsia::test]
476    async fn test_new_partition_vbmetar() -> Result<(), Error> {
477        new_partition_vbmetax_test_helper("vbmeta_r", Configuration::Recovery).await
478    }
479
480    #[fuchsia::test]
481    async fn test_new_partition_efi() -> Result<(), Error> {
482        let proxy = mock_partition("efi", 512, 1000, WORKSTATION_INSTALLER_GPT)?;
483        let part = Partition::new("efi".to_string(), proxy, BootloaderType::Efi).await?;
484        assert!(part.is_some());
485        let part = part.unwrap();
486        assert_eq!(part.pave_type, PartitionPaveType::Bootloader);
487        assert_eq!(part.size, 512 * 1000);
488        assert_eq!(part.src, "efi");
489        assert!(!part.is_ab());
490        Ok(())
491    }
492
493    #[fuchsia::test]
494    async fn test_new_partition_fvm() -> Result<(), Error> {
495        let proxy = mock_partition("storage-sparse", 2048, 4097, WORKSTATION_INSTALLER_GPT)?;
496        let part = Partition::new("storage-sparse".to_string(), proxy, BootloaderType::Efi).await?;
497        assert!(part.is_some());
498        let part = part.unwrap();
499        assert_eq!(part.pave_type, PartitionPaveType::Volume);
500        assert_eq!(part.size, 2048 * 4097);
501        assert_eq!(part.src, "storage-sparse");
502        assert!(!part.is_ab());
503        Ok(())
504    }
505
506    #[fuchsia::test]
507    async fn test_zircona_unsigned_coreboot() -> Result<(), Error> {
508        let proxy = mock_partition("zircon_a", 512, 1000, WORKSTATION_INSTALLER_GPT)?;
509        let part = Partition::new("zircon_a".to_string(), proxy, BootloaderType::Coreboot).await?;
510        assert!(part.is_none());
511        Ok(())
512    }
513
514    #[fuchsia::test]
515    async fn test_zircona_signed_coreboot() -> Result<(), Error> {
516        let proxy = mock_partition("zircon_a.signed", 512, 1000, WORKSTATION_INSTALLER_GPT)?;
517        let part =
518            Partition::new("zircon_a.signed".to_string(), proxy, BootloaderType::Coreboot).await?;
519        assert!(part.is_some());
520        let part = part.unwrap();
521        assert_eq!(
522            part.pave_type,
523            PartitionPaveType::Asset { r#type: Asset::Kernel, config: Configuration::A }
524        );
525        assert_eq!(part.size, 512 * 1000);
526        assert_eq!(part.src, "zircon_a.signed");
527        assert!(part.is_ab());
528        Ok(())
529    }
530
531    #[fuchsia::test]
532    async fn test_new_partition_unknown() -> Result<(), Error> {
533        let proxy = mock_partition("unknown-label", 512, 1000, WORKSTATION_INSTALLER_GPT)?;
534        let part = Partition::new("unknown-label".to_string(), proxy, BootloaderType::Efi).await?;
535        assert!(part.is_none());
536        Ok(())
537    }
538
539    #[fuchsia::test]
540    async fn test_new_partition_zedboot_efi() -> Result<(), Error> {
541        let proxy = mock_partition("zedboot-efi", 512, 1000, WORKSTATION_INSTALLER_GPT)?;
542        let part = Partition::new("zedboot-efi".to_string(), proxy, BootloaderType::Efi).await?;
543        assert!(part.is_none());
544        Ok(())
545    }
546
547    #[fuchsia::test]
548    async fn test_invalid_partitions_coreboot() -> Result<(), Error> {
549        let proxy = mock_partition("zircon_.signed", 512, 1000, WORKSTATION_INSTALLER_GPT)?;
550        let part =
551            Partition::new("zircon_.signed".to_string(), proxy, BootloaderType::Coreboot).await?;
552        assert!(part.is_none());
553
554        let proxy = mock_partition("zircon_aa.signed", 512, 1000, WORKSTATION_INSTALLER_GPT)?;
555        let part =
556            Partition::new("zircon_aa.signed".to_string(), proxy, BootloaderType::Coreboot).await?;
557        assert!(part.is_none());
558
559        Ok(())
560    }
561
562    #[fuchsia::test]
563    async fn test_invalid_partitions_efi() -> Result<(), Error> {
564        let proxy = mock_partition("zircon_", 512, 1000, WORKSTATION_INSTALLER_GPT)?;
565        let part = Partition::new("zircon_".to_string(), proxy, BootloaderType::Efi).await?;
566        assert!(part.is_none());
567
568        let proxy = mock_partition("zircon_aa", 512, 1000, WORKSTATION_INSTALLER_GPT)?;
569        let part = Partition::new("zircon_aa".to_string(), proxy, BootloaderType::Efi).await?;
570        assert!(part.is_none());
571
572        let proxy = mock_partition("zircon_a.signed", 512, 1000, WORKSTATION_INSTALLER_GPT)?;
573        let part =
574            Partition::new("zircon_a.signed".to_string(), proxy, BootloaderType::Efi).await?;
575        assert!(part.is_none());
576        Ok(())
577    }
578
579    #[fuchsia::test]
580    async fn test_new_partition_usb_bad_guid() -> Result<(), Error> {
581        let proxy = mock_partition("zircon_a", 512, 1000, [0xaa; 16])?;
582        let part = Partition::new("/dev/usb-bus".to_string(), proxy, BootloaderType::Efi).await?;
583        assert!(part.is_none());
584        Ok(())
585    }
586
587    #[fuchsia::test]
588    async fn test_new_partition_usb_zircona() -> Result<(), Error> {
589        let proxy = mock_partition("zircon_a", 512, 1000, WORKSTATION_PARTITION_GPTS[2])?;
590        let part = Partition::new("/dev/usb-bus".to_string(), proxy, BootloaderType::Efi).await?;
591        assert!(part.is_some());
592        let part = part.unwrap();
593        assert_eq!(
594            part.pave_type,
595            PartitionPaveType::Asset { r#type: Asset::Kernel, config: Configuration::A }
596        );
597        assert_eq!(part.size, 512 * 1000);
598        assert_eq!(part.src, "/dev/usb-bus");
599        assert!(part.is_ab());
600        Ok(())
601    }
602
603    #[fuchsia::test]
604    async fn test_new_partition_usb_zirconb() -> Result<(), Error> {
605        let proxy = mock_partition("zircon_b", 20, 1000, WORKSTATION_PARTITION_GPTS[3])?;
606        let part = Partition::new("/dev/usb-bus".to_string(), proxy, BootloaderType::Efi).await?;
607        assert!(part.is_some());
608        let part = part.unwrap();
609        assert_eq!(
610            part.pave_type,
611            PartitionPaveType::Asset { r#type: Asset::Kernel, config: Configuration::A }
612        );
613        assert_eq!(part.size, 20 * 1000);
614        assert_eq!(part.src, "/dev/usb-bus");
615        assert!(part.is_ab());
616        Ok(())
617    }
618
619    #[fuchsia::test]
620    async fn test_new_partition_usb_zirconr() -> Result<(), Error> {
621        let proxy = mock_partition("zircon_r", 40, 200, WORKSTATION_PARTITION_GPTS[4])?;
622        let part = Partition::new("/dev/usb-bus".to_string(), proxy, BootloaderType::Efi).await?;
623        assert!(part.is_some());
624        let part = part.unwrap();
625        assert_eq!(
626            part.pave_type,
627            PartitionPaveType::Asset { r#type: Asset::Kernel, config: Configuration::Recovery }
628        );
629        assert_eq!(part.size, 40 * 200);
630        assert_eq!(part.src, "/dev/usb-bus");
631        assert!(!part.is_ab());
632        Ok(())
633    }
634
635    #[fuchsia::test]
636    async fn test_new_partition_usb_efi() -> Result<(), Error> {
637        let proxy = mock_partition("efi-system", 512, 1000, WORKSTATION_PARTITION_GPTS[0])?;
638        let part = Partition::new("/dev/usb-bus".to_string(), proxy, BootloaderType::Efi).await?;
639        assert!(part.is_some());
640        let part = part.unwrap();
641        assert_eq!(part.pave_type, PartitionPaveType::Bootloader);
642        assert_eq!(part.size, 512 * 1000);
643        assert_eq!(part.src, "/dev/usb-bus");
644        assert!(!part.is_ab());
645        Ok(())
646    }
647}