builtins/
factory_items.rs1use 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
14static 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 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 Ok(Arc::new(FactoryItems { items: HashMap::new() }))
47 }
48 Err(err) => {
49 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 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 assert!(items.is_ok());
117 assert_eq!(items.unwrap().items.len(), 0);
118 }
119
120 #[fuchsia::test]
121 async fn duplicate_factory_item() {
122 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 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 assert!(!rights.contains(zx::Rights::WRITE));
181 assert!(!rights.contains(zx::Rights::SET_PROPERTY));
182 }
183}