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};
12
13static IMMUTABLE_VMO_RIGHTS: LazyLock<zx::Rights> = LazyLock::new(|| {
16 zx::Rights::DUPLICATE
17 | zx::Rights::TRANSFER
18 | zx::Rights::READ
19 | zx::Rights::MAP
20 | zx::Rights::GET_PROPERTY
21});
22
23#[derive(Debug)]
24struct FactoryItem {
25 vmo: zx::Vmo,
26 length: u32,
27}
28
29#[derive(Debug)]
30pub struct FactoryItems {
31 items: HashMap<u32, FactoryItem>,
33}
34
35impl FactoryItems {
36 pub fn new(parser: &mut ZbiParser) -> Result<Arc<Self>, Error> {
37 match parser.try_get_item(StorageBootfsFactory.into_raw(), None) {
38 Ok(result) => {
39 parser.release_item(StorageBootfsFactory)?;
40 FactoryItems::from_parsed_zbi(result)
41 }
42 Err(err) if err == ZbiParserError::ItemNotFound { zbi_type: StorageBootfsFactory } => {
43 Ok(Arc::new(FactoryItems { items: HashMap::new() }))
46 }
47 Err(err) => {
48 Err(anyhow!(
50 "Failed to retrieve StorageBootfsFactory item with unexpected error: {}",
51 err
52 ))
53 }
54 }
55 }
56
57 fn from_parsed_zbi(items: Vec<ZbiResult>) -> Result<Arc<Self>, Error> {
58 let mut parsed_items = HashMap::new();
59 for item in items {
60 if parsed_items.contains_key(&item.extra) {
63 return Err(anyhow!("Duplicate factory item found in ZBI: {}", item.extra));
64 }
65
66 let vmo = zx::Vmo::create(item.bytes.len().try_into()?)?;
67 vmo.write(&item.bytes, 0)?;
68 parsed_items
69 .insert(item.extra, FactoryItem { vmo, length: item.bytes.len().try_into()? });
70 }
71
72 Ok(Arc::new(FactoryItems { items: parsed_items }))
73 }
74
75 pub async fn serve(
76 self: Arc<Self>,
77 mut stream: fboot::FactoryItemsRequestStream,
78 ) -> Result<(), Error> {
79 while let Some(fboot::FactoryItemsRequest::Get { extra, responder }) =
80 stream.try_next().await?
81 {
82 match self.items.get(&extra) {
83 Some(item) => responder
84 .send(Some(item.vmo.duplicate_handle(*IMMUTABLE_VMO_RIGHTS)?), item.length)?,
85 None => responder.send(None, 0)?,
86 };
87 }
88 Ok(())
89 }
90}
91
92#[cfg(test)]
93mod tests {
94 use super::*;
95 use fuchsia_async as fasync;
96
97 fn serve_factory_items(items: Vec<ZbiResult>) -> Result<fboot::FactoryItemsProxy, Error> {
98 let (proxy, stream) =
99 fidl::endpoints::create_proxy_and_stream::<fboot::FactoryItemsMarker>();
100 fasync::Task::local(
101 FactoryItems::from_parsed_zbi(items)?
102 .serve(stream)
103 .unwrap_or_else(|e| panic!("Error while serving factory items service: {}", e)),
104 )
105 .detach();
106 Ok(proxy)
107 }
108
109 #[fuchsia::test]
110 async fn no_factory_items_in_zbi() {
111 let mut parser = ZbiParser::new(zx::Vmo::create(0).expect("Failed to create empty VMO"));
112 let items = FactoryItems::new(&mut parser);
113
114 assert!(items.is_ok());
116 assert_eq!(items.unwrap().items.len(), 0);
117 }
118
119 #[fuchsia::test]
120 async fn duplicate_factory_item() {
121 let mock_results = vec![
124 ZbiResult { bytes: b"abc".to_vec(), extra: 12345 },
125 ZbiResult { bytes: b"def".to_vec(), extra: 12345 },
126 ZbiResult { bytes: b"ghi".to_vec(), extra: 54321 },
127 ];
128
129 let factory_items = serve_factory_items(mock_results);
130 assert!(factory_items.is_err());
131 }
132
133 #[fuchsia::test]
134 async fn no_matching_factory_item() {
135 let mock_results = vec![
136 ZbiResult { bytes: b"abc".to_vec(), extra: 123 },
137 ZbiResult { bytes: b"def".to_vec(), extra: 456 },
138 ZbiResult { bytes: b"ghi".to_vec(), extra: 789 },
139 ];
140
141 let factory_items = serve_factory_items(mock_results).unwrap();
142 let (vmo, length) =
143 factory_items.get(314159).await.expect("Failed to query factory item service");
144
145 assert!(vmo.is_none());
146 assert_eq!(length, 0);
147 }
148
149 #[fuchsia::test]
150 async fn get_factory_items_success() {
151 let mock_results = vec![
152 ZbiResult { bytes: b"abc".to_vec(), extra: 123 },
153 ZbiResult { bytes: b"def".to_vec(), extra: 456 },
154 ZbiResult { bytes: b"ghi".to_vec(), extra: 789 },
155 ];
156
157 let factory_items = serve_factory_items(mock_results).unwrap();
158 let (vmo, length) =
159 factory_items.get(456).await.expect("Failed to query factory item service");
160
161 let mut bytes = [0; b"def".len()];
162 assert_eq!(length, bytes.len() as u32);
163
164 assert!(vmo.is_some());
165 let vmo = vmo.unwrap();
166 vmo.read(&mut bytes, 0).unwrap();
167 assert_eq!(&bytes, b"def");
168
169 let rights = vmo.basic_info().unwrap().rights;
170
171 assert!(rights.contains(zx::Rights::DUPLICATE));
173 assert!(rights.contains(zx::Rights::TRANSFER));
174 assert!(rights.contains(zx::Rights::READ));
175 assert!(rights.contains(zx::Rights::MAP));
176 assert!(rights.contains(zx::Rights::GET_PROPERTY));
177
178 assert!(!rights.contains(zx::Rights::WRITE));
180 assert!(!rights.contains(zx::Rights::SET_PROPERTY));
181 }
182}