blob: 5ade61e2db17024febafb823cebc9587afeee18b [file] [log] [blame]
// Copyright 2023 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::{anyhow, Error};
use fidl::AsHandleRef;
use std::sync::{Arc, OnceLock};
use tracing::error;
use vfs::common::rights_to_posix_mode_bits;
use vfs::file::{File, FileOptions, GetVmo, SyncMode};
use vfs::immutable_attributes;
use vfs::node::IsDirectory;
use {fidl_fuchsia_io as fio, fuchsia_zircon as zx};
/// Mimics the c++ blobfs block size.
const BLOCK_SIZE: u64 = 8192;
static VMEX_RESOURCE: OnceLock<zx::Resource> = OnceLock::new();
/// Attempt to initialize the vmex resource. Without a vmex, attempts to get the backing memory
/// of a blob with executable rights will fail with NOT_SUPPORTED.
pub fn init_vmex_resource(vmex: zx::Resource) -> Result<(), Error> {
VMEX_RESOURCE.set(vmex).map_err(|_| anyhow!(zx::Status::ALREADY_BOUND))
}
/// `VmoBlob` is a wrapper around the fuchsia.io/File protocol. Represents an immutable blob on
/// Fxfs. Clients will use this library to read and execute blobs.
pub struct VmoBlob {
vmo: zx::Vmo,
}
impl VmoBlob {
pub fn new(vmo: zx::Vmo) -> Arc<Self> {
Arc::new(Self { vmo })
}
}
impl GetVmo for VmoBlob {
fn get_vmo(&self) -> &zx::Vmo {
&self.vmo
}
}
impl IsDirectory for VmoBlob {
fn is_directory(&self) -> bool {
false
}
}
impl vfs::node::Node for VmoBlob {
async fn get_attrs(&self) -> Result<fio::NodeAttributes, zx::Status> {
let content_size = self.get_size().await?;
Ok(fio::NodeAttributes {
mode: fio::MODE_TYPE_FILE
| rights_to_posix_mode_bits(/*r*/ true, /*w*/ false, /*x*/ true),
id: fio::INO_UNKNOWN,
content_size,
// TODO(https://fxbug.dev/295550170): Get storage_size from fxblob.
storage_size: ((content_size + BLOCK_SIZE - 1) / BLOCK_SIZE) * BLOCK_SIZE,
link_count: 1,
creation_time: 0,
modification_time: 0,
})
}
async fn get_attributes(
&self,
requested_attributes: fio::NodeAttributesQuery,
) -> Result<fio::NodeAttributes2, zx::Status> {
let content_size = self.get_size().await?;
Ok(immutable_attributes!(
requested_attributes,
Immutable {
protocols: fio::NodeProtocolKinds::FILE,
abilities: fio::Operations::GET_ATTRIBUTES
| fio::Operations::READ_BYTES
| fio::Operations::EXECUTE,
content_size: content_size,
// TODO(https://fxbug.dev/295550170): Get storage_size from fxblob.
storage_size: ((content_size + BLOCK_SIZE - 1) / BLOCK_SIZE) * BLOCK_SIZE,
link_count: 1,
id: fio::INO_UNKNOWN,
}
))
}
}
/// Implement VFS trait so blobs can be accessed as files.
impl File for VmoBlob {
fn executable(&self) -> bool {
true
}
async fn open_file(&self, _options: &FileOptions) -> Result<(), zx::Status> {
Ok(())
}
async fn truncate(&self, _length: u64) -> Result<(), zx::Status> {
Err(zx::Status::ACCESS_DENIED)
}
async fn get_size(&self) -> Result<u64, zx::Status> {
self.vmo.get_content_size()
}
async fn get_backing_memory(&self, flags: fio::VmoFlags) -> Result<zx::Vmo, zx::Status> {
let vmo_size = self.get_size().await?;
if vmo_size == 0 {
// Mimics c++ blobfs behavior.
return Err(zx::Status::BAD_STATE);
}
// We do not support exact/duplicate sharing mode.
if flags.contains(fio::VmoFlags::SHARED_BUFFER) {
error!("get_backing_memory does not support exact sharing mode!");
return Err(zx::Status::NOT_SUPPORTED);
}
// We only support the combination of WRITE when a private COW clone is explicitly
// specified. This implicitly restricts any mmap call that attempts to use MAP_SHARED +
// PROT_WRITE.
if flags.contains(fio::VmoFlags::WRITE) && !flags.contains(fio::VmoFlags::PRIVATE_CLONE) {
error!("get_buffer only supports VmoFlags::WRITE with VmoFlags::PRIVATE_CLONE!");
return Err(zx::Status::NOT_SUPPORTED);
}
let mut child_options = zx::VmoChildOptions::SNAPSHOT_AT_LEAST_ON_WRITE;
// By default, SNAPSHOT includes WRITE, so we explicitly remove it if not required.
if !flags.contains(fio::VmoFlags::WRITE) {
child_options |= zx::VmoChildOptions::NO_WRITE
}
let mut child_vmo = self.vmo.create_child(child_options, 0, vmo_size)?;
if flags.contains(fio::VmoFlags::EXECUTE) {
// TODO(https://fxbug.dev/293606235): Filter out other flags.
child_vmo = child_vmo
.replace_as_executable(VMEX_RESOURCE.get().ok_or(zx::Status::NOT_SUPPORTED)?)?;
}
Ok(child_vmo)
}
async fn set_attrs(
&self,
_flags: fio::NodeAttributeFlags,
_attrs: fio::NodeAttributes,
) -> Result<(), zx::Status> {
Err(zx::Status::ACCESS_DENIED)
}
async fn update_attributes(
&self,
_attributes: fio::MutableNodeAttributes,
) -> Result<(), zx::Status> {
Err(zx::Status::ACCESS_DENIED)
}
async fn sync(&self, _mode: SyncMode) -> Result<(), zx::Status> {
Ok(())
}
fn event(&self) -> Result<Option<zx::Event>, zx::Status> {
let event = zx::Event::create();
// The file is immediately readable (see `fuchsia.io2.File.Describe`).
event.signal_handle(zx::Signals::empty(), zx::Signals::USER_0)?;
Ok(Some(event))
}
}