1use super::device_storage::DeviceStorage;
6use super::fidl_storage::FidlStorage;
7use crate::private::Sealed;
8use crate::stash_logger::StashInspectLogger;
9use anyhow::{format_err, Error};
10use fidl::endpoints::create_proxy;
11use fidl_fuchsia_io::DirectoryProxy;
12use fidl_fuchsia_stash::StoreProxy;
13use futures::lock::Mutex;
14use futures::Future;
15use std::any::Any;
16use std::collections::HashMap;
17use std::rc::Rc;
18
19pub trait DefaultLoader {
20 type Result;
21 fn default_value(&self) -> Self::Result;
22}
23impl<T> Sealed for T where T: DefaultLoader {}
24
25pub struct NoneT;
27impl Sealed for NoneT {}
28
29pub trait StorageFactory {
32 type Storage;
34
35 fn initialize<T>(&self) -> impl Future<Output = Result<(), Error>>
38 where
39 T: StorageAccess<Storage = Self::Storage>;
40
41 fn initialize_with_loader<T, L>(&self, loader: L) -> impl Future<Output = Result<(), Error>>
44 where
45 T: StorageAccess<Storage = Self::Storage>,
46 L: DefaultLoader<Result = T::Data> + 'static;
47
48 fn get_store(&self) -> impl Future<Output = Rc<Self::Storage>>;
50}
51
52pub trait StorageAccess {
72 type Storage;
73 type Data;
74
75 const STORAGE_KEY: &'static str;
77}
78
79pub enum InitializationState<T, U = ()> {
83 Initializing(HashMap<&'static str, Option<Box<dyn Any>>>, U),
87 Partial,
92 Initialized(Rc<T>),
95}
96
97impl<T> InitializationState<T, ()> {
98 pub fn new() -> Self {
100 Self::Initializing(HashMap::new(), ())
101 }
102}
103
104impl<T, U> Default for InitializationState<T, U>
105where
106 U: Default,
107{
108 fn default() -> Self {
109 Self::Initializing(Default::default(), Default::default())
110 }
111}
112
113impl<T> InitializationState<T, DirectoryProxy> {
114 pub fn with_storage_dir(storage_dir: DirectoryProxy) -> Self {
116 Self::Initializing(HashMap::new(), storage_dir)
117 }
118}
119
120pub struct StashDeviceStorageFactory {
122 store: StoreProxy,
123 device_storage_cache: Mutex<InitializationState<DeviceStorage>>,
124 inspect_handle: Rc<Mutex<StashInspectLogger>>,
125}
126
127impl StashDeviceStorageFactory {
128 pub fn new(
130 store: StoreProxy,
131 inspect_handle: Rc<Mutex<StashInspectLogger>>,
132 ) -> StashDeviceStorageFactory {
133 StashDeviceStorageFactory {
134 store,
135 device_storage_cache: Mutex::new(InitializationState::new()),
136 inspect_handle,
137 }
138 }
139
140 async fn initialize_storage(&self, key: &'static str) -> Result<(), Error> {
142 match &mut *self.device_storage_cache.lock().await {
143 InitializationState::Initializing(initial_keys, ()) => {
144 let _ = initial_keys.insert(key, None);
145 Ok(())
146 }
147 InitializationState::Initialized(_) => {
148 Err(format_err!("Cannot initialize an already accessed device storage"))
149 }
150 _ => unreachable!(),
151 }
152 }
153
154 async fn initialize_storage_with_loader(
156 &self,
157 key: &'static str,
158 loader: Box<dyn Any>,
159 ) -> Result<(), Error> {
160 match &mut *self.device_storage_cache.lock().await {
161 InitializationState::Initializing(initial_keys, ()) => {
162 let _ = initial_keys.insert(key, Some(loader));
163 Ok(())
164 }
165 InitializationState::Initialized(_) => {
166 Err(format_err!("Cannot initialize an already accessed device storage"))
167 }
168 _ => unreachable!(),
169 }
170 }
171}
172
173impl StorageFactory for StashDeviceStorageFactory {
174 type Storage = DeviceStorage;
175
176 async fn initialize<T>(&self) -> Result<(), Error>
177 where
178 T: StorageAccess<Storage = DeviceStorage>,
179 {
180 self.initialize_storage(T::STORAGE_KEY).await
181 }
182
183 async fn initialize_with_loader<T, L>(&self, loader: L) -> Result<(), Error>
184 where
185 T: StorageAccess<Storage = DeviceStorage>,
186 L: DefaultLoader<Result = T::Data> + 'static,
187 {
188 self.initialize_storage_with_loader(T::STORAGE_KEY, Box::new(loader) as Box<dyn Any>).await
189 }
190
191 async fn get_store(&self) -> Rc<DeviceStorage> {
192 let initialization = &mut *self.device_storage_cache.lock().await;
193 match initialization {
194 InitializationState::Initializing(initial_keys, ()) => {
195 let device_storage = Rc::new(DeviceStorage::with_stash_proxy(
196 initial_keys.drain(),
197 || {
198 let (accessor_proxy, server_end) = create_proxy();
199 self.store
200 .create_accessor(false, server_end)
201 .expect("failed to create accessor for stash");
202 accessor_proxy
203 },
204 Rc::clone(&self.inspect_handle),
205 ));
206 *initialization = InitializationState::Initialized(Rc::clone(&device_storage));
207 device_storage
208 }
209 InitializationState::Initialized(device_storage) => Rc::clone(device_storage),
210 _ => unreachable!(),
211 }
212 }
213}
214
215pub struct FidlStorageFactory {
217 migration_id: u64,
218 device_storage_cache: Mutex<InitializationState<FidlStorage, DirectoryProxy>>,
219}
220
221impl FidlStorageFactory {
222 pub fn new(migration_id: u64, storage_dir: DirectoryProxy) -> FidlStorageFactory {
224 FidlStorageFactory {
225 migration_id,
226 device_storage_cache: Mutex::new(InitializationState::with_storage_dir(storage_dir)),
227 }
228 }
229
230 async fn initialize_storage(&self, key: &'static str) -> Result<(), Error> {
232 match &mut *self.device_storage_cache.lock().await {
233 InitializationState::Initializing(initial_keys, _) => {
234 let _ = initial_keys.insert(key, None);
235 Ok(())
236 }
237 InitializationState::Initialized(_) => {
238 Err(format_err!("Cannot initialize an already accessed device storage"))
239 }
240 _ => unreachable!(),
241 }
242 }
243
244 async fn initialize_storage_with_loader(
246 &self,
247 key: &'static str,
248 loader: Box<dyn Any>,
249 ) -> Result<(), Error> {
250 match &mut *self.device_storage_cache.lock().await {
251 InitializationState::Initializing(initial_keys, _) => {
252 let _ = initial_keys.insert(key, Some(loader));
253 Ok(())
254 }
255 InitializationState::Initialized(_) => {
256 Err(format_err!("Cannot initialize an already accessed device storage"))
257 }
258 _ => unreachable!(),
259 }
260 }
261}
262
263impl StorageFactory for FidlStorageFactory {
264 type Storage = FidlStorage;
265
266 async fn initialize<T>(&self) -> Result<(), Error>
267 where
268 T: StorageAccess<Storage = FidlStorage>,
269 {
270 self.initialize_storage(T::STORAGE_KEY).await
271 }
272
273 async fn initialize_with_loader<T, L>(&self, loader: L) -> Result<(), Error>
274 where
275 T: StorageAccess<Storage = FidlStorage>,
276 L: DefaultLoader<Result = T::Data> + 'static,
277 {
278 self.initialize_storage_with_loader(T::STORAGE_KEY, Box::new(loader) as Box<dyn Any>).await
279 }
280
281 async fn get_store(&self) -> Rc<FidlStorage> {
282 let initialization = &mut *self.device_storage_cache.lock().await;
283 match initialization {
284 InitializationState::Initializing(..) => {
285 let (initial_keys, storage_dir) =
286 match std::mem::replace(initialization, InitializationState::Partial) {
287 InitializationState::Initializing(initial_keys, storage_dir) => {
288 (initial_keys, storage_dir)
289 }
290 _ => unreachable!(),
291 };
292 let migration_id = self.migration_id;
293 let (device_storage, sync_tasks) =
294 FidlStorage::with_file_proxy(initial_keys, storage_dir, move |key| {
295 let temp_file_name = format!("{key}.tmp");
296 let file_name = format!("{key}_{migration_id}.pfidl");
297 Ok((temp_file_name, file_name))
298 })
299 .await
300 .expect("failed to get storage");
301 for task in sync_tasks {
302 task.detach();
303 }
304
305 let device_storage = Rc::new(device_storage);
306 *initialization = InitializationState::Initialized(Rc::clone(&device_storage));
307 device_storage
308 }
309 InitializationState::Initialized(device_storage) => Rc::clone(device_storage),
310 _ => unreachable!(),
311 }
312 }
313}