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