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 fidl::AsHandleRef;
97 use fuchsia_async as fasync;
98
99 fn serve_factory_items(items: Vec<ZbiResult>) -> Result<fboot::FactoryItemsProxy, Error> {
100 let (proxy, stream) =
101 fidl::endpoints::create_proxy_and_stream::<fboot::FactoryItemsMarker>();
102 fasync::Task::local(
103 FactoryItems::from_parsed_zbi(items)?
104 .serve(stream)
105 .unwrap_or_else(|e| panic!("Error while serving factory items service: {}", e)),
106 )
107 .detach();
108 Ok(proxy)
109 }
110
111 #[fuchsia::test]
112 async fn no_factory_items_in_zbi() {
113 let mut parser = ZbiParser::new(zx::Vmo::create(0).expect("Failed to create empty VMO"));
114 let items = FactoryItems::new(&mut parser);
115
116 assert!(items.is_ok());
118 assert_eq!(items.unwrap().items.len(), 0);
119 }
120
121 #[fuchsia::test]
122 async fn duplicate_factory_item() {
123 let mock_results = vec![
126 ZbiResult { bytes: b"abc".to_vec(), extra: 12345 },
127 ZbiResult { bytes: b"def".to_vec(), extra: 12345 },
128 ZbiResult { bytes: b"ghi".to_vec(), extra: 54321 },
129 ];
130
131 let factory_items = serve_factory_items(mock_results);
132 assert!(factory_items.is_err());
133 }
134
135 #[fuchsia::test]
136 async fn no_matching_factory_item() {
137 let mock_results = vec![
138 ZbiResult { bytes: b"abc".to_vec(), extra: 123 },
139 ZbiResult { bytes: b"def".to_vec(), extra: 456 },
140 ZbiResult { bytes: b"ghi".to_vec(), extra: 789 },
141 ];
142
143 let factory_items = serve_factory_items(mock_results).unwrap();
144 let (vmo, length) =
145 factory_items.get(314159).await.expect("Failed to query factory item service");
146
147 assert!(vmo.is_none());
148 assert_eq!(length, 0);
149 }
150
151 #[fuchsia::test]
152 async fn get_factory_items_success() {
153 let mock_results = vec![
154 ZbiResult { bytes: b"abc".to_vec(), extra: 123 },
155 ZbiResult { bytes: b"def".to_vec(), extra: 456 },
156 ZbiResult { bytes: b"ghi".to_vec(), extra: 789 },
157 ];
158
159 let factory_items = serve_factory_items(mock_results).unwrap();
160 let (vmo, length) =
161 factory_items.get(456).await.expect("Failed to query factory item service");
162
163 let mut bytes = [0; b"def".len()];
164 assert_eq!(length, bytes.len() as u32);
165
166 assert!(vmo.is_some());
167 let vmo = vmo.unwrap();
168 vmo.read(&mut bytes, 0).unwrap();
169 assert_eq!(&bytes, b"def");
170
171 let rights = vmo.basic_info().unwrap().rights;
172
173 assert!(rights.contains(zx::Rights::DUPLICATE));
175 assert!(rights.contains(zx::Rights::TRANSFER));
176 assert!(rights.contains(zx::Rights::READ));
177 assert!(rights.contains(zx::Rights::MAP));
178 assert!(rights.contains(zx::Rights::GET_PROPERTY));
179
180 assert!(!rights.contains(zx::Rights::WRITE));
182 assert!(!rights.contains(zx::Rights::SET_PROPERTY));
183 }
184}