Skip to main content

fxfs_platform/fuchsia/
component.rs

1// Copyright 2022 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use 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
61/// Runs Fxfs as a component.
62pub struct Component {
63    state: futures::lock::Mutex<State>,
64
65    // The execution scope of the pseudo filesystem.
66    scope: ExecutionScope,
67
68    // The root of the pseudo filesystem for the component.
69    export_dir: Arc<vfs::directory::immutable::Simple>,
70}
71
72// Wrapper type to add `FsInspect` support to an `OpenFxFilesystem`.
73struct InspectedFxFilesystem(OpenFxFilesystem, /*fs_id=*/ 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            // TODO(https://fxbug.dev/42175930): Should these be moved to per-volume nodes?
110            total_nodes: 0,
111            used_nodes: 0,
112        }
113    }
114}
115
116struct RunningState {
117    // We have to wrap this in an Arc, even though it itself basically just wraps an Arc, so that
118    // FsInspectTree can reference `fs` as a Weak<dyn FsInspect>`.
119    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", /* must_be_directory: */ false);
136            let _ = outgoing_dir.remove_entry("debug", /* must_be_directory: */ 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    /// Runs Fxfs as a component.
153    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        // TODO(b/315704445): Only enable in debug builds?
215        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        // TODO(https://fxbug.dev/42174810): This is not very graceful.  It would be better for the client to
295        // explicitly shut down all volumes first, and make this fail if there are remaining active
296        // connections.  Fix the bug in fs_test which requires this.
297        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        // TODO(https://fxbug.dev/42063349) Add support for block sizes greater than the page size.
326        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            /* overwrite: */ 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            // Unwrap ok, shouldn't have anything else recording or replaying this early in startup.
396            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?, /* read_only: */ 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, /* read_only: */ 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        // TODO(b/311550633): Stash ok res in inspect.
443        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    // Returns true if we should close the connection.
457    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    /// Handles fuchsia.fxfs.Debug requests, providing live debugging internals of the running
475    /// filesystem.
476    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                // This should fail whilst the volume is mounted.
776                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                // Creating another volume with the same name should fail.
787                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                // Removing a non-existent volume should fail.
798                volumes_proxy
799                    .remove("test")
800                    .await
801                    .expect("fidl failed")
802                    .expect_err("remove failed");
803
804                // Create the same volume again and it should now succeed.
805                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        // Verify the GUID
887        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}