blob: fbc9b52216c509e50e0bfdd5c2ec387b976b5f9e [file]
// Copyright 2026 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 fidl_fuchsia_erofs::{ErofsMarker, ErofsServeRequest};
use fidl_fuchsia_io as fio;
use fuchsia_component::client::connect_to_protocol_sync;
use starnix_core::fs::fuchsia::RemoteFs;
use starnix_core::mm::ProtectionFlags;
use starnix_core::task::CurrentTask;
use starnix_core::vfs::{CacheMode, FileSystem, FileSystemHandle, FileSystemOptions};
use starnix_logging::{impossible_error, log_error, log_info};
use starnix_uapi::errors::Errno;
use starnix_uapi::mount_flags::FileSystemFlags;
use starnix_uapi::open_flags::OpenFlags;
use starnix_uapi::{errno, from_status_like_fdio};
use std::sync::atomic::Ordering;
use zx::{MonotonicInstant, VmoChildOptions};
pub fn new_fs(
current_task: &CurrentTask,
options: FileSystemOptions,
) -> Result<FileSystemHandle, Errno> {
let kernel = current_task.kernel();
// EROFS is strictly read-only.
if !options.flags.load(Ordering::Relaxed).contains(FileSystemFlags::RDONLY) {
return Err(errno!(EROFS));
}
let fsoffset = options.params.get_as::<u64>(b"fsoffset")?;
let source_device = current_task.open_file(options.source.as_ref(), OpenFlags::RDONLY)?;
let memory = source_device.get_memory(current_task, None, ProtectionFlags::READ)?;
let backing_vmo = memory
.as_vmo()
.ok_or_else(|| errno!(EINVAL))?
.duplicate_handle(zx::Rights::SAME_RIGHTS)
.map_err(impossible_error)?;
let backing_vmo = match fsoffset {
None => {
log_info!("mounting erofs image source={:?}", options.source);
backing_vmo
}
Some(fsoffset) if fsoffset > 0 => {
let page_size = zx::system_get_page_size() as u64;
if fsoffset % page_size != 0 {
log_error!("fsoffset {} is not page-aligned (page size {})", fsoffset, page_size);
return Err(errno!(EINVAL));
}
let vmo_size = backing_vmo.get_size().map_err(impossible_error)?;
let clone_len = vmo_size.checked_sub(fsoffset).ok_or_else(|| {
log_error!("fsoffset {} exceeds vmo size {}", fsoffset, vmo_size);
errno!(EINVAL)
})?;
log_info!("mounting erofs image with fsoffset={}, len={}", fsoffset, clone_len);
backing_vmo.create_child(VmoChildOptions::SLICE, fsoffset, clone_len).map_err(|e| {
log_error!("Failed to create child VMO slice for fsoffset {}: {:?}", fsoffset, e);
errno!(EINVAL)
})?
}
Some(_) => {
log_info!("mounting erofs image with fsoffset of zero");
backing_vmo
}
};
let erofs_provider = connect_to_protocol_sync::<ErofsMarker>().map_err(|_| errno!(ENOENT))?;
let (root_client, root_server) = fidl::endpoints::create_endpoints();
erofs_provider
.serve(
ErofsServeRequest {
backing_vmo: Some(backing_vmo),
root: Some(root_server),
..Default::default()
},
MonotonicInstant::INFINITE,
)
.map_err(|e| {
log_error!("FIDL error on Erofs.Serve: {e:?}");
errno!(EIO)
})?
.map_err(|e| {
log_error!("Erofs.Serve failed: {e:?}");
from_status_like_fdio!(zx::Status::err_from_raw(e))
})?;
let (remotefs, root_node, info, node_id) =
RemoteFs::new(root_client.into_channel(), fio::PERM_READABLE, "erofs")?;
let fs =
FileSystem::new(kernel, CacheMode::Cached(kernel.fs_cache_config()), remotefs, options)?;
fs.create_root_with_info(node_id, root_node, info);
Ok(fs)
}