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