builtins/
factory_items.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 anyhow::{Error, anyhow};
6use fidl_fuchsia_boot as fboot;
7use fuchsia_zbi::ZbiType::StorageBootfsFactory;
8use fuchsia_zbi::{ZbiParser, ZbiParserError, ZbiResult};
9use futures::prelude::*;
10use std::collections::HashMap;
11use std::sync::{Arc, LazyLock};
12use zx::{self as zx, HandleBased};
13
14// The default rights for an immutable VMO. For details see
15// https://fuchsia.dev/fuchsia-src/reference/syscalls/vmo_create#description.
16static IMMUTABLE_VMO_RIGHTS: LazyLock<zx::Rights> = LazyLock::new(|| {
17    zx::Rights::DUPLICATE
18        | zx::Rights::TRANSFER
19        | zx::Rights::READ
20        | zx::Rights::MAP
21        | zx::Rights::GET_PROPERTY
22});
23
24#[derive(Debug)]
25struct FactoryItem {
26    vmo: zx::Vmo,
27    length: u32,
28}
29
30#[derive(Debug)]
31pub struct FactoryItems {
32    // The key of this HashMap is the "extra" field of the zbi_header_t.
33    items: HashMap<u32, FactoryItem>,
34}
35
36impl FactoryItems {
37    pub fn new(parser: &mut ZbiParser) -> Result<Arc<Self>, Error> {
38        match parser.try_get_item(StorageBootfsFactory.into_raw(), None) {
39            Ok(result) => {
40                parser.release_item(StorageBootfsFactory)?;
41                FactoryItems::from_parsed_zbi(result)
42            }
43            Err(err) if err == ZbiParserError::ItemNotFound { zbi_type: StorageBootfsFactory } => {
44                // It's not an unexpected error to not have any StorageBootfsFactory items in the
45                // ZBI. This service will just return None to any queries.
46                Ok(Arc::new(FactoryItems { items: HashMap::new() }))
47            }
48            Err(err) => {
49                // Any error besides ItemNotFound is unexpected, and fatal.
50                Err(anyhow!(
51                    "Failed to retrieve StorageBootfsFactory item with unexpected error: {}",
52                    err
53                ))
54            }
55        }
56    }
57
58    fn from_parsed_zbi(items: Vec<ZbiResult>) -> Result<Arc<Self>, Error> {
59        let mut parsed_items = HashMap::new();
60        for item in items {
61            // The factory items service uses the ZBI extra field as a lookup key. There can
62            // be many factory items in the ZBI, but each extra field must be unique.
63            if parsed_items.contains_key(&item.extra) {
64                return Err(anyhow!("Duplicate factory item found in ZBI: {}", item.extra));
65            }
66
67            let vmo = zx::Vmo::create(item.bytes.len().try_into()?)?;
68            vmo.write(&item.bytes, 0)?;
69            parsed_items
70                .insert(item.extra, FactoryItem { vmo, length: item.bytes.len().try_into()? });
71        }
72
73        Ok(Arc::new(FactoryItems { items: parsed_items }))
74    }
75
76    pub async fn serve(
77        self: Arc<Self>,
78        mut stream: fboot::FactoryItemsRequestStream,
79    ) -> Result<(), Error> {
80        while let Some(fboot::FactoryItemsRequest::Get { extra, responder }) =
81            stream.try_next().await?
82        {
83            match self.items.get(&extra) {
84                Some(item) => responder
85                    .send(Some(item.vmo.duplicate_handle(*IMMUTABLE_VMO_RIGHTS)?), item.length)?,
86                None => responder.send(None, 0)?,
87            };
88        }
89        Ok(())
90    }
91}
92
93#[cfg(test)]
94mod tests {
95    use super::*;
96    use fuchsia_async as fasync;
97
98    fn serve_factory_items(items: Vec<ZbiResult>) -> Result<fboot::FactoryItemsProxy, Error> {
99        let (proxy, stream) =
100            fidl::endpoints::create_proxy_and_stream::<fboot::FactoryItemsMarker>();
101        fasync::Task::local(
102            FactoryItems::from_parsed_zbi(items)?
103                .serve(stream)
104                .unwrap_or_else(|e| panic!("Error while serving factory items service: {}", e)),
105        )
106        .detach();
107        Ok(proxy)
108    }
109
110    #[fuchsia::test]
111    async fn no_factory_items_in_zbi() {
112        let mut parser = ZbiParser::new(zx::Vmo::create(0).expect("Failed to create empty VMO"));
113        let items = FactoryItems::new(&mut parser);
114
115        // It's not an error for there to be no factory items in the ZBI.
116        assert!(items.is_ok());
117        assert_eq!(items.unwrap().items.len(), 0);
118    }
119
120    #[fuchsia::test]
121    async fn duplicate_factory_item() {
122        // Note that two ZbiResults have the same 'extra' value, and this value is what's used
123        // for ths service's key.
124        let mock_results = vec![
125            ZbiResult { bytes: b"abc".to_vec(), extra: 12345 },
126            ZbiResult { bytes: b"def".to_vec(), extra: 12345 },
127            ZbiResult { bytes: b"ghi".to_vec(), extra: 54321 },
128        ];
129
130        let factory_items = serve_factory_items(mock_results);
131        assert!(factory_items.is_err());
132    }
133
134    #[fuchsia::test]
135    async fn no_matching_factory_item() {
136        let mock_results = vec![
137            ZbiResult { bytes: b"abc".to_vec(), extra: 123 },
138            ZbiResult { bytes: b"def".to_vec(), extra: 456 },
139            ZbiResult { bytes: b"ghi".to_vec(), extra: 789 },
140        ];
141
142        let factory_items = serve_factory_items(mock_results).unwrap();
143        let (vmo, length) =
144            factory_items.get(314159).await.expect("Failed to query factory item service");
145
146        assert!(vmo.is_none());
147        assert_eq!(length, 0);
148    }
149
150    #[fuchsia::test]
151    async fn get_factory_items_success() {
152        let mock_results = vec![
153            ZbiResult { bytes: b"abc".to_vec(), extra: 123 },
154            ZbiResult { bytes: b"def".to_vec(), extra: 456 },
155            ZbiResult { bytes: b"ghi".to_vec(), extra: 789 },
156        ];
157
158        let factory_items = serve_factory_items(mock_results).unwrap();
159        let (vmo, length) =
160            factory_items.get(456).await.expect("Failed to query factory item service");
161
162        let mut bytes = [0; b"def".len()];
163        assert_eq!(length, bytes.len() as u32);
164
165        assert!(vmo.is_some());
166        let vmo = vmo.unwrap();
167        vmo.read(&mut bytes, 0).unwrap();
168        assert_eq!(&bytes, b"def");
169
170        let rights = vmo.basic_info().unwrap().rights;
171
172        // VMO has default immutable rights.
173        assert!(rights.contains(zx::Rights::DUPLICATE));
174        assert!(rights.contains(zx::Rights::TRANSFER));
175        assert!(rights.contains(zx::Rights::READ));
176        assert!(rights.contains(zx::Rights::MAP));
177        assert!(rights.contains(zx::Rights::GET_PROPERTY));
178
179        // VMO does not have any mutable rights.
180        assert!(!rights.contains(zx::Rights::WRITE));
181        assert!(!rights.contains(zx::Rights::SET_PROPERTY));
182    }
183}