blob: 2d6e3aa9690345aec507d93cbb4126dc3a43ec97 [file] [log] [blame]
// 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(())
}