fxfs_platform_testing/fuchsia/
remote_volume.rs1use crate::fuchsia::errors::map_to_status;
6use crate::fuchsia::file::FxFile;
7use crate::fuchsia::volume::FxVolume;
8use anyhow::Error;
9use fidl_fuchsia_fxfs as ffxfs;
10use fidl_fuchsia_io as fio;
11use fuchsia_sync::Mutex;
12use futures::StreamExt;
13use fxfs::log::error;
14use fxfs::object_store::ObjectStore;
15use std::sync::Arc;
16
17pub type UnmountFn =
18 Box<dyn FnOnce() -> futures::future::BoxFuture<'static, Result<(), Error>> + Send>;
19
20pub struct FxRemoteVolume {
23 store: Arc<ObjectStore>,
24 volume: Option<Arc<FxVolume>>,
25 unmount_fn: Mutex<Option<UnmountFn>>,
26 scope: fuchsia_async::Scope,
27}
28
29#[fxfs_trace::trace]
30impl FxRemoteVolume {
31 pub fn new(
32 store: Arc<ObjectStore>,
33 volume: Option<Arc<FxVolume>>,
34 unmount_fn: Option<UnmountFn>,
35 ) -> Arc<Self> {
36 Arc::new(Self {
37 store,
38 volume,
39 unmount_fn: Mutex::new(unmount_fn),
40 scope: fuchsia_async::Scope::new_with_name("fxfs-remote-vol"),
41 })
42 }
43
44 pub fn store(&self) -> &Arc<ObjectStore> {
45 &self.store
46 }
47
48 pub fn serve(self: &Arc<Self>, stream: ffxfs::RemoteVolumeRequestStream) {
49 let this = self.clone();
50 self.scope.spawn(async move {
51 this.handle_requests(stream).await;
52 });
53 }
54
55 pub fn shutdown(&self) {
56 let _ = self.scope.to_handle().cancel();
57 }
58
59 pub async fn wait(&self) {
60 self.scope.on_no_tasks().await;
61 }
62
63 pub async fn get_layer_set(&self) -> Result<ffxfs::LayerSet, Error> {
65 let layer_set = self.store.tree().immutable_layer_set();
66 let mut layer_files = Vec::new();
67 let rights =
68 zx::Rights::BASIC | zx::Rights::READ | zx::Rights::MAP | zx::Rights::GET_PROPERTY;
69 for layer in layer_set.layers.iter() {
70 if let Some(handle) = layer.handle() {
71 let object_id = handle.object_id();
72 let vmo = if let Some(vmo) = handle.get_vmo() {
73 vmo.duplicate_handle(rights)?
74 } else {
75 let size = handle.get_size();
76 let vmo = zx::Vmo::create(size)?;
77 vmo.set_content_size(&size)?;
78 if size > 0 {
79 let aligned_size = handle.block_size().align_up(size).unwrap() as usize;
80 let mut buf = handle.allocate_buffer(aligned_size).await;
81 let read_len = handle.read_aligned(0, buf.as_mut()).await?;
82 vmo.write(&buf.subslice(..read_len).to_vec(), 0)?;
83 }
84 vmo.replace_handle(rights)?
85 };
86 layer_files.push(ffxfs::LayerFile { object_id, vmo });
87 }
88 }
89 Ok(ffxfs::LayerSet { layers: layer_files })
90 }
91
92 async fn unmount(&self) -> Result<(), Error> {
93 let unmount_fn = self.unmount_fn.lock().take();
94 if let Some(unmount_fn) = unmount_fn {
95 unmount_fn().await?;
96 } else if let Some(volume) = &self.volume {
97 volume.terminate().await;
98 }
99 Ok(())
100 }
101
102 pub async fn handle_requests(self: Arc<Self>, mut stream: ffxfs::RemoteVolumeRequestStream) {
104 let mut unmounted = false;
105 while let Some(request) = stream.next().await {
106 match request {
107 Ok(ffxfs::RemoteVolumeRequest::OpenFile { object_id, file, responder }) => {
108 let res: Result<fio::FileInfo, zx::Status> = async {
109 let volume = self.volume.as_ref().ok_or(zx::Status::NOT_SUPPORTED)?;
110 let opened_node =
112 volume.open_file_by_id(object_id).await.map_err(map_to_status)?;
113 let stream = zx::Stream::create(
114 zx::StreamOptions::MODE_READ | zx::StreamOptions::MODE_WRITE,
115 opened_node.vmo(),
116 0,
117 )?;
118 let client_stream = stream.duplicate_handle(zx::Rights::SAME_RIGHTS)?;
119 let file_info =
120 fio::FileInfo { stream: Some(client_stream), ..Default::default() };
121 let scope = volume.scope().clone();
122 let flags =
123 fio::Flags::PROTOCOL_FILE | fio::PERM_READABLE | fio::PERM_WRITABLE;
124 let mut request = vfs::ObjectRequest::new(
125 flags,
126 &fio::Options::default(),
127 file.into_channel(),
128 );
129 FxFile::create_connection_with_stream_async(
130 opened_node,
131 scope,
132 flags,
133 (&mut request).into(),
134 stream,
135 )
136 .await?;
137 Ok(file_info)
138 }
139 .await;
140 let _ = responder.send(res.map_err(|s| s.into_raw()));
141 }
142
143 Ok(ffxfs::RemoteVolumeRequest::Close { responder }) => {
144 let res = self.unmount().await;
145 let _ = responder.send(res.map_err(|e| map_to_status(e).into_raw()));
146 self.shutdown();
149 unmounted = true;
150 break;
151 }
152
153 Err(e) => {
154 error!(e:?; "RemoteVolume request stream error");
155 break;
156 }
157 }
158 }
159 if !unmounted {
160 let _ = self.unmount().await;
161 self.shutdown();
162 }
163 }
164}
165
166#[cfg(test)]
167mod tests {
168 use super::*;
169 use fidl::endpoints::create_proxy;
170 use fxfs::filesystem::FxFilesystemBuilder;
171 use fxfs::object_handle::{ObjectHandle, WriteObjectHandle};
172 use fxfs::object_store::transaction::Options;
173 use fxfs::object_store::volume::root_volume;
174 use fxfs_insecure_crypto::new_insecure_crypt;
175 use storage_device::DeviceHolder;
176 use storage_device::fake_device::FakeDevice;
177
178 fn new_test_fs_builder() -> FxFilesystemBuilder {
179 FxFilesystemBuilder::new()
180 }
181
182 #[fuchsia::test]
183 async fn test_remote_volume_layer_set() {
184 let device = DeviceHolder::new(FakeDevice::new(16384, 512));
185 let fs = new_test_fs_builder().format(true).open(device).await.unwrap();
186 let root_vol = root_volume(fs.clone()).await.unwrap();
187 let store = root_vol.new_volume("test_remote_vol", Default::default()).await.unwrap();
188
189 let mut transaction = store
190 .new_transaction(
191 fxfs::object_store::transaction::LockKeys::default(),
192 Options::default(),
193 )
194 .await
195 .unwrap();
196 let _file = ObjectStore::create_object(
197 &store,
198 &mut transaction,
199 fxfs::object_store::HandleOptions::default(),
200 None,
201 )
202 .await
203 .unwrap();
204 transaction.commit().await.unwrap();
205 store.flush().await.expect("flush failed");
206
207 let remote_vol = FxRemoteVolume::new(store.clone(), None, None);
208 let layer_set = remote_vol.get_layer_set().await.expect("get_layer_set failed");
209 assert!(!layer_set.layers.is_empty());
210
211 fs.close().await.expect("close failed");
212 }
213
214 #[fuchsia::test]
215 async fn test_remote_volume_close_fidl() {
216 use std::sync::atomic::{AtomicBool, Ordering};
217
218 let device = DeviceHolder::new(FakeDevice::new(16384, 512));
219 let fs = new_test_fs_builder().format(true).open(device).await.unwrap();
220 let root_vol = root_volume(fs.clone()).await.unwrap();
221 let store = root_vol.new_volume("test_remote_vol", Default::default()).await.unwrap();
222
223 let unmounted = Arc::new(AtomicBool::new(false));
224 let unmounted_clone = unmounted.clone();
225 let unmount_fn: UnmountFn = Box::new(move || {
226 Box::pin(async move {
227 unmounted_clone.store(true, Ordering::Relaxed);
228 Ok(())
229 })
230 });
231
232 let remote_vol = FxRemoteVolume::new(store.clone(), None, Some(unmount_fn));
233
234 let (proxy, server_end) = create_proxy::<ffxfs::RemoteVolumeMarker>();
235 remote_vol.serve(server_end.into_stream());
236
237 assert!(!unmounted.load(Ordering::Relaxed));
238
239 let close_res = proxy.close().await.expect("close FIDL call failed");
241 assert!(close_res.is_ok());
242
243 assert!(unmounted.load(Ordering::Relaxed));
245
246 fs.close().await.expect("close failed");
247 }
248
249 #[fuchsia::test(threads = 10)]
250 async fn test_remote_volume_open_file() {
251 const WRAPPING_KEY_ID: [u8; 16] = [1; 16];
252
253 let device = DeviceHolder::new(FakeDevice::new(16384, 512));
254 let fs = new_test_fs_builder().format(true).open(device).await.unwrap();
255 let crypt = Arc::new(new_insecure_crypt());
256 crypt.add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into()).unwrap();
257
258 let root_vol = root_volume(fs.clone()).await.unwrap();
259 let store = root_vol
260 .new_volume(
261 "test_remote_vol",
262 fxfs::object_store::NewChildStoreOptions {
263 options: fxfs::object_store::StoreOptions {
264 crypt: Some(crypt.clone()),
265 ..Default::default()
266 },
267 ..Default::default()
268 },
269 )
270 .await
271 .unwrap();
272
273 let (client_end, server_end) =
274 fidl::endpoints::create_endpoints::<ffxfs::RemoteVolumeMarker>();
275 let proxy = Arc::new(ffxfs::RemoteVolumeSynchronousProxy::new(client_end.into_channel()));
276 let fx_volume = Arc::new(
277 FxVolume::new(
278 std::sync::Weak::new(),
279 store.clone(),
280 zx::Event::create().koid().unwrap().raw_koid(),
281 "test_vol_server".to_string(),
282 Arc::new(refaults_vmo::PageFaultCounter::new().unwrap()),
283 Default::default(),
284 )
285 .unwrap(),
286 );
287
288 let remote_vol = FxRemoteVolume::new(store.clone(), Some(fx_volume.clone()), None);
289 remote_vol.serve(server_end.into_stream());
290
291 let mut transaction = store
292 .new_transaction(
293 fxfs::object_store::transaction::LockKeys::default(),
294 Options::default(),
295 )
296 .await
297 .unwrap();
298 let fscrypt_file = ObjectStore::create_object(
299 &store,
300 &mut transaction,
301 fxfs::object_store::HandleOptions::default(),
302 Some(WRAPPING_KEY_ID.into()),
303 )
304 .await
305 .unwrap();
306 let fscrypt_file_id = fscrypt_file.object_id();
307 transaction.commit().await.unwrap();
308
309 let payload = b"encrypted file read test";
310 let mut buf = fscrypt_file.allocate_buffer(payload.len()).await;
311 buf.as_mut_ptr_slice().copy_from_slice(payload);
312 fscrypt_file.write_or_append(Some(0), buf.as_ref()).await.unwrap();
313
314 let (file_proxy, file_server_end) = create_proxy::<fio::FileMarker>();
315 let file_info = proxy
316 .open_file(fscrypt_file_id, file_server_end, zx::MonotonicInstant::INFINITE)
317 .unwrap()
318 .expect("open_file failed");
319 let stream = file_info.stream.expect("Stream expected in FileInfo");
320 let stream_clone = stream.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap();
321 let read_bytes = fuchsia_async::unblock(move || {
322 stream_clone
323 .read_at_to_vec(zx::StreamReadOptions::empty(), 0, payload.len())
324 .expect("stream read failed")
325 })
326 .await;
327 assert_eq!(&read_bytes, payload);
328
329 drop(stream);
330 drop(file_proxy);
331 drop(proxy);
332
333 remote_vol.wait().await;
334 fs.close().await.expect("close failed");
335 }
336}