1use crate::fuchsia::debug::{create_debug_directory, handle_debug_request};
6use crate::fuchsia::errors::map_to_status;
7use crate::fuchsia::memory_pressure::MemoryPressureMonitor;
8use crate::fuchsia::power::FuchsiaPowerManager;
9use crate::fuchsia::volume::MemoryPressureConfig;
10use crate::fuchsia::volumes_directory::VolumesDirectory;
11use anyhow::{Context, Error, bail};
12use async_trait::async_trait;
13use block_client::{BlockClient as _, RemoteBlockClient};
14use fidl::endpoints::{ClientEnd, DiscoverableProtocolMarker, RequestStream};
15use fidl_fuchsia_fs::{AdminMarker, AdminRequest, AdminRequestStream};
16use fidl_fuchsia_fs_startup::{
17 CheckOptions, StartOptions, StartupMarker, StartupRequest, StartupRequestStream, VolumesMarker,
18 VolumesRequest, VolumesRequestStream,
19};
20use fidl_fuchsia_fxfs::{
21 DebugMarker, DebugRequestStream, VolumeInstallerMarker, VolumeInstallerRequest,
22 VolumeInstallerRequestStream,
23};
24use fidl_fuchsia_io as fio;
25use fidl_fuchsia_memory_attribution as fattribution;
26use fidl_fuchsia_process_lifecycle::{LifecycleRequest, LifecycleRequestStream};
27use fidl_fuchsia_storage_block::BlockMarker;
28use fs_inspect::{FsInspect, FsInspectTree, InfoData, UsageData};
29use fuchsia_async as fasync;
30use fuchsia_component_client::connect_to_protocol;
31use futures::TryStreamExt;
32use futures::lock::Mutex;
33use fxfs::filesystem::{FxFilesystem, FxFilesystemBuilder, MIN_BLOCK_SIZE, OpenFxFilesystem, mkfs};
34use fxfs::log::*;
35use fxfs::object_store::volume::root_volume;
36use fxfs::serialized_types::LATEST_VERSION;
37use fxfs::{fsck, metrics};
38use fxfs_trace::{TraceFutureExt, trace_future_args};
39use refaults_vmo::PageRefaultCounter;
40use std::ops::Deref;
41use std::sync::{Arc, Weak};
42use storage_device::DeviceHolder;
43use storage_device::block_device::BlockDevice;
44use vfs::directory::helper::DirectlyMutable;
45use vfs::execution_scope::ExecutionScope;
46
47const FXFS_INFO_NAME: &'static str = "fxfs";
48
49pub fn map_to_raw_status(e: Error) -> zx::sys::zx_status_t {
50 map_to_status(e).into_raw()
51}
52
53pub async fn new_block_client(remote: ClientEnd<BlockMarker>) -> Result<RemoteBlockClient, Error> {
54 Ok(RemoteBlockClient::new(remote.into_proxy()).await?)
55}
56
57pub struct Component {
59 state: futures::lock::Mutex<State>,
60
61 scope: ExecutionScope,
63
64 export_dir: Arc<vfs::directory::immutable::Simple>,
66}
67
68struct InspectedFxFilesystem(OpenFxFilesystem, u64);
70
71impl From<OpenFxFilesystem> for InspectedFxFilesystem {
72 fn from(fs: OpenFxFilesystem) -> Self {
73 Self(fs, zx::Event::create().koid().unwrap().raw_koid())
74 }
75}
76
77impl Deref for InspectedFxFilesystem {
78 type Target = Arc<FxFilesystem>;
79 fn deref(&self) -> &Self::Target {
80 &self.0.deref()
81 }
82}
83
84#[async_trait]
85impl FsInspect for InspectedFxFilesystem {
86 fn get_info_data(&self) -> InfoData {
87 let earliest_version = self.0.super_block_header().earliest_version;
88 InfoData {
89 id: self.1,
90 fs_type: fidl_fuchsia_fs::VfsType::Fxfs.into_primitive().into(),
91 name: FXFS_INFO_NAME.into(),
92 version_major: LATEST_VERSION.major.into(),
93 version_minor: LATEST_VERSION.minor.into(),
94 block_size: self.0.block_size() as u64,
95 max_filename_length: fio::MAX_NAME_LENGTH,
96 oldest_version: Some(format!("{}.{}", earliest_version.major, earliest_version.minor)),
97 }
98 }
99
100 async fn get_usage_data(&self) -> UsageData {
101 let info = self.0.get_info();
102 UsageData {
103 total_bytes: info.total_bytes,
104 used_bytes: info.used_bytes,
105 total_nodes: 0,
107 used_nodes: 0,
108 }
109 }
110}
111
112struct RunningState {
113 fs: Arc<InspectedFxFilesystem>,
116 volumes: Arc<VolumesDirectory>,
117 _inspect_tree: Arc<FsInspectTree>,
118}
119
120enum State {
121 ComponentStarted,
122 Running(RunningState),
123}
124
125impl State {
126 async fn stop(&mut self, outgoing_dir: &vfs::directory::immutable::Simple) {
127 if let State::Running(RunningState { fs, volumes, .. }) =
128 std::mem::replace(self, State::ComponentStarted)
129 {
130 info!("Stopping Fxfs runtime; remaining connections will be forcibly closed");
131 let _ = outgoing_dir.remove_entry("volumes", false);
132 let _ = outgoing_dir.remove_entry("debug", false);
133 volumes.terminate().await;
134 let _ = fs.deref().close().await;
135 }
136 }
137}
138
139impl Component {
140 pub fn new() -> Arc<Self> {
141 Arc::new(Self {
142 state: Mutex::new(State::ComponentStarted),
143 scope: ExecutionScope::new(),
144 export_dir: vfs::directory::immutable::simple(),
145 })
146 }
147
148 pub async fn run(
150 self: Arc<Self>,
151 outgoing_dir: zx::Channel,
152 lifecycle_channel: Option<zx::Channel>,
153 ) -> Result<(), Error> {
154 let svc_dir = vfs::directory::immutable::simple();
155 self.export_dir.add_entry("svc", svc_dir.clone()).expect("Unable to create svc dir");
156
157 let weak = Arc::downgrade(&self);
158 svc_dir.add_entry(
159 StartupMarker::PROTOCOL_NAME,
160 vfs::service::host(move |requests| {
161 let weak = weak.clone();
162 async move {
163 if let Some(me) = weak.upgrade() {
164 let _ = me.handle_startup_requests(requests).await;
165 }
166 }
167 }),
168 )?;
169 let weak = Arc::downgrade(&self);
170 svc_dir.add_entry(
171 VolumesMarker::PROTOCOL_NAME,
172 vfs::service::host(move |requests| {
173 let weak = weak.clone();
174 async move {
175 if let Some(me) = weak.upgrade() {
176 me.handle_volumes_requests(requests).await;
177 }
178 }
179 }),
180 )?;
181
182 let weak = Arc::downgrade(&self);
183 svc_dir.add_entry(
184 AdminMarker::PROTOCOL_NAME,
185 vfs::service::host(move |requests| {
186 let weak = weak.clone();
187 async move {
188 if let Some(me) = weak.upgrade() {
189 let _ = me.handle_admin_requests(requests).await;
190 }
191 }
192 }),
193 )?;
194
195 let weak = Arc::downgrade(&self);
196 svc_dir.add_entry(
197 VolumeInstallerMarker::PROTOCOL_NAME,
198 vfs::service::host(move |requests| {
199 let weak = weak.clone();
200 async move {
201 if let Some(me) = weak.upgrade() {
202 if let Err(error) = me.handle_volume_installer_requests(requests).await {
203 error!(error:?; "failed to handle VolumeInstaller requests");
204 }
205 }
206 }
207 }),
208 )?;
209
210 let weak = Arc::downgrade(&self);
212 svc_dir.add_entry(
213 DebugMarker::PROTOCOL_NAME,
214 vfs::service::host(move |requests| {
215 let weak = weak.clone();
216 async move {
217 if let Some(me) = weak.upgrade() {
218 let _ = me.handle_debug_requests(requests).await;
219 }
220 }
221 }),
222 )?;
223
224 vfs::directory::serve_on(
225 self.export_dir.clone(),
226 fio::PERM_READABLE | fio::PERM_WRITABLE | fio::PERM_EXECUTABLE,
227 self.scope.clone(),
228 fidl::endpoints::ServerEnd::new(outgoing_dir),
229 );
230
231 if let Some(channel) = lifecycle_channel {
232 let me = self.clone();
233 self.scope.spawn(async move {
234 if let Err(error) = me.handle_lifecycle_requests(channel).await {
235 warn!(error:?; "handle_lifecycle_requests");
236 }
237 });
238 }
239
240 self.scope.wait().await;
241
242 Ok(())
243 }
244
245 async fn handle_startup_requests(&self, mut stream: StartupRequestStream) -> Result<(), Error> {
246 while let Some(request) = stream.try_next().await? {
247 match request {
248 StartupRequest::Start { responder, device, options } => {
249 async move {
250 responder.send(self.handle_start(device, options).await.map_err(|error| {
251 error!(error:?; "handle_start failed");
252 map_to_raw_status(error)
253 }))
254 }
255 .trace(trace_future_args!("Startup::Start"))
256 .await?
257 }
258 StartupRequest::Format { responder, device, .. } => {
259 async move {
260 responder.send(self.handle_format(device).await.map_err(|error| {
261 error!(error:?; "handle_format failed");
262 map_to_raw_status(error)
263 }))
264 }
265 .trace(trace_future_args!("Startup::Format"))
266 .await?
267 }
268 StartupRequest::Check { responder, device, options } => {
269 async move {
270 responder.send(self.handle_check(device, options).await.map_err(|error| {
271 error!(error:?; "handle_check failed");
272 map_to_raw_status(error)
273 }))
274 }
275 .trace(trace_future_args!("Startup::Check"))
276 .await?
277 }
278 }
279 }
280 Ok(())
281 }
282
283 async fn handle_start(
284 &self,
285 device: ClientEnd<BlockMarker>,
286 options: StartOptions,
287 ) -> Result<(), Error> {
288 info!(options:?; "Received start request");
289 let mut state = self.state.lock().await;
290 state.stop(&self.export_dir).await;
294 let client = new_block_client(device).await?;
295
296 assert!(client.block_size() <= zx::system_get_page_size());
298 assert!((zx::system_get_page_size() as u64) == MIN_BLOCK_SIZE);
299
300 let fs = FxFilesystemBuilder::new()
301 .fsck_after_every_transaction(options.fsck_after_every_transaction.unwrap_or(false))
302 .read_only(options.read_only.unwrap_or(false))
303 .inline_crypto_enabled(options.inline_crypto_enabled.unwrap_or(false))
304 .barriers_enabled(options.barriers_enabled.unwrap_or(false))
305 .power_manager(FuchsiaPowerManager::new())
306 .open(DeviceHolder::new(
307 BlockDevice::new(client, options.read_only.unwrap_or(false)).await?,
308 ))
309 .await?;
310 let root_volume = root_volume(fs.clone()).await?;
311 let fs: Arc<InspectedFxFilesystem> = Arc::new(fs.into());
312 let weak_fs = Arc::downgrade(&fs) as Weak<dyn FsInspect + Send + Sync>;
313 let inspect_tree =
314 Arc::new(FsInspectTree::new(weak_fs, fuchsia_inspect::component::inspector().root()));
315
316 let blob_resupplied_count = Arc::new(PageRefaultCounter::new()?);
317 connect_to_protocol::<fattribution::PageRefaultSinkMarker>()?
318 .send_page_refault_count(blob_resupplied_count.readonly_vmo()?)?;
319
320 let mem_monitor = match MemoryPressureMonitor::start() {
321 Ok(v) => Some(v),
322 Err(error) => {
323 warn!(
324 error:?;
325 "Failed to connect to memory pressure monitor. Running \
326 without pressure awareness."
327 );
328 None
329 }
330 };
331
332 let volumes = VolumesDirectory::new(
333 root_volume,
334 Arc::downgrade(&inspect_tree),
335 mem_monitor,
336 blob_resupplied_count,
337 MemoryPressureConfig::default(),
338 )
339 .await?;
340
341 self.export_dir.add_entry_may_overwrite(
342 "volumes",
343 volumes.directory_node().clone(),
344 true,
345 )?;
346
347 let debug = create_debug_directory(&**fs, &volumes);
348 self.export_dir.add_entry_may_overwrite("debug", debug, true)?;
349
350 fs.allocator().track_statistics(&metrics::detail(), "allocator");
351 fs.journal().track_statistics(&metrics::detail(), "journal");
352 fs.object_manager().track_statistics(&metrics::detail(), "object_manager");
353
354 let info = fs.get_info();
355 info!(
356 device_size = info.total_bytes,
357 used = info.used_bytes,
358 free = info.total_bytes - info.used_bytes;
359 "Mounted"
360 );
361
362 if let Some(profile_time) = options.startup_profiling_seconds {
363 volumes
365 .record_and_replay_profile(None, ".boot".to_owned(), profile_time)
366 .await
367 .unwrap();
368 }
369
370 *state = State::Running(RunningState { fs, volumes, _inspect_tree: inspect_tree });
371
372 Ok(())
373 }
374
375 async fn handle_format(&self, device: ClientEnd<BlockMarker>) -> Result<(), Error> {
376 let device = DeviceHolder::new(
377 BlockDevice::new(new_block_client(device).await?, false).await?,
378 );
379 mkfs(device).await?;
380 info!("Formatted filesystem");
381 Ok(())
382 }
383
384 async fn handle_check(
385 &self,
386 device: ClientEnd<BlockMarker>,
387 _options: CheckOptions,
388 ) -> Result<(), Error> {
389 let state = self.state.lock().await;
390 let (fs_container, fs) = match *state {
391 State::ComponentStarted => {
392 let client = new_block_client(device).await?;
393 let fs_container = FxFilesystemBuilder::new()
394 .read_only(true)
395 .open(DeviceHolder::new(BlockDevice::new(client, true).await?))
396 .await?;
397 let fs = fs_container.clone();
398 (Some(fs_container), fs)
399 }
400 State::Running(RunningState { ref fs, .. }) => (None, fs.deref().deref().clone()),
401 };
402 let res = fsck::fsck(fs.clone()).await;
403 if let Some(fs_container) = fs_container {
404 let _ = fs_container.close().await;
405 }
406 info!("handle_check for fs: {:?}", res?);
408 Ok(())
409 }
410
411 async fn handle_admin_requests(&self, mut stream: AdminRequestStream) -> Result<(), Error> {
412 while let Some(request) = stream.try_next().await.context("Reading request")? {
413 if self.handle_admin(request).await? {
414 break;
415 }
416 }
417 Ok(())
418 }
419
420 async fn handle_admin(&self, req: AdminRequest) -> Result<bool, Error> {
422 match req {
423 AdminRequest::Shutdown { responder } => {
424 async move {
425 info!("Received shutdown request");
426 self.shutdown().await;
427 responder
428 .send()
429 .unwrap_or_else(|error| warn!(error:?; "Failed to send shutdown response"));
430 }
431 .trace(trace_future_args!("Admin::Shutdown"))
432 .await;
433 return Ok(true);
434 }
435 }
436 }
437
438 async fn handle_debug_requests(&self, mut stream: DebugRequestStream) -> Result<(), Error> {
441 while let Some(request) = stream.try_next().await.context("Reading request")? {
442 let state = self.state.lock().await;
443 let (fs, volumes) = match &*state {
444 State::ComponentStarted => {
445 info!("Debug commands are not valid unless component is started.");
446 bail!("Component not started");
447 }
448 State::Running(RunningState { fs, volumes, .. }) => {
449 (fs.deref().deref().clone(), volumes.clone())
450 }
451 };
452 handle_debug_request(fs, volumes, request).await?;
453 }
454 Ok(())
455 }
456
457 async fn shutdown(&self) {
458 self.state.lock().await.stop(&self.export_dir).await;
459 info!("Filesystem terminated");
460 }
461
462 async fn handle_volumes_requests(&self, mut stream: VolumesRequestStream) {
463 let volumes =
464 if let State::Running(RunningState { volumes, .. }) = &*self.state.lock().await {
465 volumes.clone()
466 } else {
467 let _ = stream.into_inner().0.shutdown_with_epitaph(zx::Status::BAD_STATE);
468 return;
469 };
470 while let Ok(Some(request)) = stream.try_next().await {
471 match request {
472 VolumesRequest::Create {
473 name,
474 outgoing_directory,
475 create_options,
476 mount_options,
477 responder,
478 } => {
479 async {
480 info!(
481 name = name.as_str();
482 "Create {}volume",
483 if mount_options.crypt.is_some() { "encrypted " } else { "" }
484 );
485 responder
486 .send(
487 volumes
488 .create_and_serve_volume(
489 &name,
490 outgoing_directory.into_channel().into(),
491 mount_options,
492 create_options,
493 )
494 .await
495 .map_err(map_to_raw_status),
496 )
497 .unwrap_or_else(
498 |error| warn!(error:?; "Failed to send volume creation response"),
499 );
500 }
501 .trace(trace_future_args!("Volumes::Create"))
502 .await;
503 }
504 VolumesRequest::Remove { name, responder } => {
505 async {
506 info!(name = name.as_str(); "Remove volume");
507 responder
508 .send(volumes.remove_volume(&name).await.map_err(map_to_raw_status))
509 .unwrap_or_else(
510 |error| warn!(error:?; "Failed to send volume removal response"),
511 );
512 }
513 .trace(trace_future_args!("Volumes::Remove"))
514 .await;
515 }
516 VolumesRequest::GetInfo { responder } => {
517 async move {
518 responder.send(Err(zx::Status::NOT_SUPPORTED.into_raw())).unwrap_or_else(
519 |error| warn!(error:?; "Failed to send volume removal response"),
520 )
521 }
522 .trace(trace_future_args!("Volumes::GetInfo"))
523 .await;
524 }
525 }
526 }
527 }
528
529 async fn handle_lifecycle_requests(&self, lifecycle_channel: zx::Channel) -> Result<(), Error> {
530 let mut stream =
531 LifecycleRequestStream::from_channel(fasync::Channel::from_channel(lifecycle_channel));
532 match stream.try_next().await.context("Reading request")? {
533 Some(LifecycleRequest::Stop { .. }) => {
534 info!("Received Lifecycle::Stop request");
535 self.shutdown().await;
536 }
537 None => {}
538 }
539 Ok(())
540 }
541
542 async fn handle_volume_installer_requests(
543 &self,
544 mut stream: VolumeInstallerRequestStream,
545 ) -> Result<(), Error> {
546 let volumes =
547 if let State::Running(RunningState { volumes, .. }) = &*self.state.lock().await {
548 volumes.clone()
549 } else {
550 let _ = stream.into_inner().0.shutdown_with_epitaph(zx::Status::BAD_STATE);
551 return Err(zx::Status::BAD_STATE).context("fxfs component not running");
552 };
553 while let Some(request) = stream.try_next().await.context("reading request")? {
554 match request {
555 VolumeInstallerRequest::Install { src, image_file, dst, responder } => {
556 async {
557 let response = volumes.install_volume(&src, &image_file, &dst).await;
558 responder.send(response.map_err(|error| {
559 error!(error:?; "install failed");
560 map_to_raw_status(error)
561 }))
562 }
563 .trace(trace_future_args!("VolumeInstaller::Install"))
564 .await?;
565 }
566 }
567 }
568 Ok(())
569 }
570}
571
572#[cfg(test)]
573mod tests {
574 use super::{Component, new_block_client};
575 use fidl::endpoints::Proxy;
576 use fidl_fuchsia_fs::AdminMarker;
577 use fidl_fuchsia_fs_startup::{
578 CreateOptions, MountOptions, StartOptions, StartupMarker, VolumesMarker,
579 };
580 use fidl_fuchsia_io as fio;
581 use fidl_fuchsia_process_lifecycle::{LifecycleMarker, LifecycleProxy};
582 use fuchsia_async as fasync;
583 use fuchsia_component_client::connect_to_protocol_at_dir_svc;
584 use fuchsia_fs::directory::readdir;
585 use futures::future::{BoxFuture, FusedFuture, FutureExt};
586 use futures::{pin_mut, select};
587 use fxfs::filesystem::FxFilesystem;
588 use fxfs::object_store::volume::root_volume;
589 use fxfs::object_store::{NewChildStoreOptions, StoreOptions};
590 use std::pin::Pin;
591 use std::sync::Arc;
592 use storage_device::DeviceHolder;
593 use storage_device::block_device::BlockDevice;
594 use test_vmo_backed_block_server::VmoBackedServer;
595
596 async fn run_test(
597 callback: impl Fn(&fio::DirectoryProxy, LifecycleProxy) -> BoxFuture<'static, ()>,
598 ) -> (Pin<Box<impl FusedFuture>>, Arc<VmoBackedServer>) {
599 const BLOCK_SIZE: u32 = 512;
600 let block_server = Arc::new(
601 VmoBackedServer::new(16384, BLOCK_SIZE, &[]).expect("Failed to create VmoBackedServer"),
602 );
603
604 {
605 let fs = FxFilesystem::new_empty(DeviceHolder::new(
606 BlockDevice::new(
607 new_block_client(block_server.connect())
608 .await
609 .expect("Unable to create block client"),
610 false,
611 )
612 .await
613 .unwrap(),
614 ))
615 .await
616 .expect("FxFilesystem::new_empty failed");
617 {
618 let root_volume = root_volume(fs.clone()).await.expect("Open root_volume failed");
619 root_volume
620 .new_volume("default", NewChildStoreOptions::default())
621 .await
622 .expect("Create volume failed");
623 }
624 fs.close().await.expect("close failed");
625 }
626
627 let (client_end, server_end) = fidl::endpoints::create_proxy::<fio::DirectoryMarker>();
628
629 let (lifecycle_client, lifecycle_server) =
630 fidl::endpoints::create_proxy::<LifecycleMarker>();
631
632 let mut component_task = Box::pin(
633 async {
634 Component::new()
635 .run(server_end.into_channel(), Some(lifecycle_server.into_channel()))
636 .await
637 .expect("Failed to run component");
638 }
639 .fuse(),
640 );
641
642 let startup_proxy = connect_to_protocol_at_dir_svc::<StartupMarker>(&client_end)
643 .expect("Unable to connect to Startup protocol");
644 let block_server_connection = block_server.connect();
645 let task = async {
646 startup_proxy
647 .start(block_server_connection, &StartOptions::default())
648 .await
649 .expect("Start failed (FIDL)")
650 .expect("Start failed");
651 callback(&client_end, lifecycle_client).await;
652 }
653 .fuse();
654
655 pin_mut!(task);
656
657 loop {
658 select! {
659 () = component_task => {},
660 () = task => break,
661 }
662 }
663
664 (component_task, block_server)
665 }
666
667 #[fuchsia::test(threads = 2)]
668 async fn test_shutdown() {
669 let (component_task, _block_server) = run_test(|client, _| {
670 let admin_proxy = connect_to_protocol_at_dir_svc::<AdminMarker>(client)
671 .expect("Unable to connect to Admin protocol");
672 async move {
673 admin_proxy.shutdown().await.expect("shutdown failed");
674 }
675 .boxed()
676 })
677 .await;
678 assert!(!component_task.is_terminated());
679 }
680
681 #[fuchsia::test(threads = 2)]
682 async fn test_lifecycle_stop() {
683 let (component_task, _block_server) = run_test(|_, lifecycle_client| {
684 lifecycle_client.stop().expect("Stop failed");
685 async move {
686 fasync::OnSignals::new(
687 &lifecycle_client.into_channel().expect("into_channel failed"),
688 zx::Signals::CHANNEL_PEER_CLOSED,
689 )
690 .await
691 .expect("OnSignals failed");
692 }
693 .boxed()
694 })
695 .await;
696 component_task.await;
697 }
698
699 #[fuchsia::test(threads = 2)]
700 async fn test_create_and_remove() {
701 let (component_task, _block_server) = run_test(|client, _| {
702 let volumes_proxy = connect_to_protocol_at_dir_svc::<VolumesMarker>(client)
703 .expect("Unable to connect to Volumes protocol");
704
705 let fs_admin_proxy = connect_to_protocol_at_dir_svc::<AdminMarker>(client)
706 .expect("Unable to connect to Admin protocol");
707
708 async move {
709 let (dir_proxy, server_end) =
710 fidl::endpoints::create_proxy::<fio::DirectoryMarker>();
711 volumes_proxy
712 .create("test", server_end, CreateOptions::default(), MountOptions::default())
713 .await
714 .expect("fidl failed")
715 .expect("create failed");
716
717 volumes_proxy
719 .remove("test")
720 .await
721 .expect("fidl failed")
722 .expect_err("remove succeeded");
723
724 let volume_admin_proxy = connect_to_protocol_at_dir_svc::<AdminMarker>(&dir_proxy)
725 .expect("Unable to connect to Admin protocol");
726 volume_admin_proxy.shutdown().await.expect("shutdown failed");
727
728 let (_dir_proxy, server_end) =
730 fidl::endpoints::create_proxy::<fio::DirectoryMarker>();
731 volumes_proxy
732 .create("test", server_end, CreateOptions::default(), MountOptions::default())
733 .await
734 .expect("fidl failed")
735 .expect_err("create succeeded");
736
737 volumes_proxy.remove("test").await.expect("fidl failed").expect("remove failed");
738
739 volumes_proxy
741 .remove("test")
742 .await
743 .expect("fidl failed")
744 .expect_err("remove failed");
745
746 let (_dir_proxy, server_end) =
748 fidl::endpoints::create_proxy::<fio::DirectoryMarker>();
749 volumes_proxy
750 .create("test", server_end, CreateOptions::default(), MountOptions::default())
751 .await
752 .expect("fidl failed")
753 .expect("create failed");
754
755 fs_admin_proxy.shutdown().await.expect("shutdown failed");
756 }
757 .boxed()
758 })
759 .await;
760 component_task.await;
761 }
762
763 #[fuchsia::test(threads = 2)]
764 async fn test_volumes_enumeration() {
765 let (component_task, _block_server) = run_test(|client, _| {
766 let volumes_proxy = connect_to_protocol_at_dir_svc::<VolumesMarker>(client)
767 .expect("Unable to connect to Volumes protocol");
768
769 let (volumes_dir_proxy, server_end) =
770 fidl::endpoints::create_proxy::<fio::DirectoryMarker>();
771 client
772 .open("volumes", fio::PERM_READABLE, &Default::default(), server_end.into_channel())
773 .expect("open failed");
774
775 let fs_admin_proxy = connect_to_protocol_at_dir_svc::<AdminMarker>(client)
776 .expect("Unable to connect to Admin protocol");
777
778 async move {
779 let (_dir_proxy, server_end) =
780 fidl::endpoints::create_proxy::<fio::DirectoryMarker>();
781 volumes_proxy
782 .create("test", server_end, CreateOptions::default(), MountOptions::default())
783 .await
784 .expect("fidl failed")
785 .expect("create failed");
786
787 let entries = readdir(&volumes_dir_proxy).await.expect("readdir failed");
788 let mut entry_names = entries.iter().map(|d| d.name.as_str()).collect::<Vec<_>>();
789 entry_names.sort();
790 assert_eq!(entry_names, ["default", "test"]);
791
792 fs_admin_proxy.shutdown().await.expect("shutdown failed");
793 }
794 .boxed()
795 })
796 .await;
797 component_task.await;
798 }
799
800 #[fuchsia::test(threads = 2)]
801 async fn test_create_with_guid() {
802 let (component_task, block_server) = run_test(|client, _| {
803 let volumes_proxy = connect_to_protocol_at_dir_svc::<VolumesMarker>(client)
804 .expect("Unable to connect to Volumes protocol");
805 let admin_proxy = connect_to_protocol_at_dir_svc::<AdminMarker>(client)
806 .expect("Unable to connect to Admin protocol");
807 async move {
808 let (_dir_proxy, server_end) =
809 fidl::endpoints::create_proxy::<fio::DirectoryMarker>();
810 let guid = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];
811 volumes_proxy
812 .create(
813 "test_guid",
814 server_end,
815 CreateOptions { guid: Some(guid), ..Default::default() },
816 MountOptions::default(),
817 )
818 .await
819 .expect("fidl failed")
820 .expect("create failed");
821 admin_proxy.shutdown().await.expect("shutdown failed");
822 }
823 .boxed()
824 })
825 .await;
826 component_task.await;
827
828 let fs = FxFilesystem::open(DeviceHolder::new(
830 BlockDevice::new(
831 new_block_client(block_server.connect())
832 .await
833 .expect("Unable to create block client"),
834 false,
835 )
836 .await
837 .unwrap(),
838 ))
839 .await
840 .expect("FxFilesystem::open failed");
841 {
842 let root_volume = root_volume(fs.clone()).await.expect("Open root_volume failed");
843 let vol = root_volume
844 .volume("test_guid", StoreOptions::default())
845 .await
846 .expect("Open volume failed");
847 assert_eq!(vol.guid(), [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]);
848 }
849 fs.close().await.expect("close failed");
850 }
851}