| // Copyright 2020 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| use { |
| anyhow::{format_err, Context, Error}, |
| fidl_fuchsia_fs::{AdminRequestStream, QueryRequestStream}, |
| fidl_fuchsia_io::{self as fio, DirectoryMarker}, |
| fuchsia_async as fasync, |
| fuchsia_component::server::ServiceFs, |
| fuchsia_fatfs::FatFs, |
| fuchsia_runtime::HandleType, |
| fuchsia_syslog::{self, fx_log_err}, |
| fuchsia_zircon::{self as zx, Status}, |
| futures::future::TryFutureExt, |
| futures::stream::{StreamExt, TryStreamExt}, |
| remote_block_device::RemoteBlockDevice, |
| std::sync::Arc, |
| vfs::{execution_scope::ExecutionScope, path::Path, registry::token_registry}, |
| }; |
| |
| enum Services { |
| Admin(AdminRequestStream), |
| Query(QueryRequestStream), |
| } |
| |
| async fn handle(stream: Services, fs: Arc<FatFs>, scope: &ExecutionScope) -> Result<(), Error> { |
| match stream { |
| Services::Admin(mut stream) => { |
| while let Some(request) = stream.try_next().await.context("Reading request")? { |
| fs.handle_admin(scope, request)?; |
| } |
| } |
| Services::Query(mut stream) => { |
| while let Some(request) = stream.try_next().await.context("Reading request")? { |
| fs.handle_query(scope, request)?; |
| } |
| } |
| } |
| |
| Ok(()) |
| } |
| |
| #[fasync::run(10)] |
| async fn main() -> Result<(), Error> { |
| fuchsia_syslog::init().unwrap(); |
| |
| // Open the remote block device. |
| let device = Box::new(remote_block_device::Cache::new(RemoteBlockDevice::new_sync( |
| zx::Channel::from( |
| fuchsia_runtime::take_startup_handle(fuchsia_runtime::HandleInfo::new( |
| HandleType::User0, |
| 1, |
| )) |
| .ok_or(format_err!("Missing device handle"))?, |
| ), |
| )?)?); |
| |
| // VFS initialization. |
| let registry = token_registry::Simple::new(); |
| let scope = ExecutionScope::build().token_registry(registry).new(); |
| |
| // Start the filesystem and open the root directory. |
| let fatfs = FatFs::new(device).map_err(|_| Status::IO)?; |
| let (proxy, server) = fidl::endpoints::create_proxy::<DirectoryMarker>()?; |
| let root = fatfs.get_root()?; |
| root.clone().open( |
| scope.clone(), |
| fio::OPEN_RIGHT_READABLE | fio::OPEN_RIGHT_WRITABLE, |
| 0, |
| Path::empty(), |
| server.into_channel().into(), |
| ); |
| |
| // Export the root directory in our outgoing directory. |
| let mut fs = ServiceFs::new(); |
| fs.add_remote("root", proxy); |
| fs.dir("svc").add_fidl_service(Services::Admin).add_fidl_service(Services::Query); |
| fs.take_and_serve_directory_handle()?; |
| |
| let fatfs = Arc::new(fatfs); |
| |
| // Handle all ServiceFs connections. VFS connections will be spawned as separate tasks. |
| const MAX_CONCURRENT: usize = 10_000; |
| fs.for_each_concurrent(MAX_CONCURRENT, |request| { |
| handle(request, Arc::clone(&fatfs), &scope).unwrap_or_else(|e| fx_log_err!("{:?}", e)) |
| }) |
| .await; |
| |
| // At this point all direct connections to ServiceFs will have been closed (and cannot be |
| // resurrected), but before we finish, we must wait for all VFS connections to be closed. |
| scope.wait().await; |
| |
| root.close().unwrap_or_else(|e| fx_log_err!("Failed to close root: {:?}", e)); |
| |
| // Make sure that fatfs has been cleanly shut down. |
| fatfs.shut_down().unwrap_or_else(|e| fx_log_err!("Failed to shutdown fatfs: {:?}", e)); |
| |
| Ok(()) |
| } |