blob: 61f45a1535963dd92fc12c55e274b9072910abaf [file] [log] [blame]
// Copyright 2021 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 crate::security;
use crate::task::{CurrentTask, Kernel};
use crate::vfs::{
CacheMode, FileHandle, FileObject, FileOps, FileSystem, FileSystemHandle, FileSystemOps,
FileSystemOptions, FsNode, FsNodeInfo, FsNodeOps, FsStr, FsString, fs_node_impl_not_dir,
};
use starnix_sync::{FileOpsCore, LockEqualOrBefore, Locked};
use starnix_types::vfs::default_statfs;
use starnix_uapi::errors::Errno;
use starnix_uapi::file_mode::FileMode;
use starnix_uapi::open_flags::OpenFlags;
use starnix_uapi::{ANON_INODE_FS_MAGIC, error, statfs};
pub struct Anon {
/// If this instance represents an `anon_inode` then `name` holds the type-name of the node,
/// e.g. "inotify", "sync_file", "[usereventfd]", etc.
name: Option<&'static str>,
is_private: bool,
}
impl FsNodeOps for Anon {
fs_node_impl_not_dir!();
fn create_file_ops(
&self,
_locked: &mut Locked<FileOpsCore>,
_node: &FsNode,
_current_task: &CurrentTask,
_flags: OpenFlags,
) -> Result<Box<dyn FileOps>, Errno> {
error!(ENOSYS)
}
fn internal_name(&self, _node: &FsNode) -> Option<FsString> {
self.name.map(|name| format!("anon_inode:{}", name).into())
}
}
impl Anon {
/// Returns a new `Anon` instance for use in a binder device FD.
pub fn new_for_binder_device() -> Self {
Self { name: None, is_private: false }
}
/// Returns a new `Anon` instance for use as the `FsNodeOps` of a socket.
pub fn new_for_socket(kernel_private: bool) -> Self {
Self { name: None, is_private: kernel_private }
}
/// Returns a new anonymous file with the specified properties, and a unique `FsNode`.
pub fn new_file_extended<L>(
locked: &mut Locked<L>,
current_task: &CurrentTask,
ops: Box<dyn FileOps>,
flags: OpenFlags,
name: &'static str,
info: FsNodeInfo,
) -> Result<FileHandle, Errno>
where
L: LockEqualOrBefore<FileOpsCore>,
{
let fs = anon_fs(locked, current_task.kernel());
let node =
fs.create_node_and_allocate_node_id(Anon { name: Some(name), is_private: false }, info);
security::fs_node_init_anon(current_task, &node, name)?;
Ok(FileObject::new_anonymous(locked, current_task, ops, node, flags))
}
/// Returns a new anonymous file with the specified properties, and a unique `FsNode`.
pub fn new_file<L>(
locked: &mut Locked<L>,
current_task: &CurrentTask,
ops: Box<dyn FileOps>,
flags: OpenFlags,
name: &'static str,
) -> Result<FileHandle, Errno>
where
L: LockEqualOrBefore<FileOpsCore>,
{
Self::new_file_extended(
locked,
current_task,
ops,
flags,
name,
FsNodeInfo::new(FileMode::from_bits(0o600), current_task.current_fscred()),
)
}
/// Returns a new anonymous file backed by a single "private" `FsNode`, to which no security
/// labeling nor access-checks will be applied.
pub fn new_private_file<L>(
locked: &mut Locked<L>,
current_task: &CurrentTask,
ops: Box<dyn FileOps>,
flags: OpenFlags,
name: &'static str,
) -> FileHandle
where
L: LockEqualOrBefore<FileOpsCore>,
{
Self::new_private_file_extended(
locked,
current_task,
ops,
flags,
name,
FsNodeInfo::new(FileMode::from_bits(0o600), current_task.current_fscred()),
)
}
/// Returns a new private anonymous file, applying caller-supplied `info`.
pub fn new_private_file_extended<L>(
locked: &mut Locked<L>,
current_task: &CurrentTask,
ops: Box<dyn FileOps>,
flags: OpenFlags,
name: &'static str,
info: FsNodeInfo,
) -> FileHandle
where
L: LockEqualOrBefore<FileOpsCore>,
{
let fs = anon_fs(locked, current_task.kernel());
let node =
fs.create_node_and_allocate_node_id(Anon { name: Some(name), is_private: true }, info);
security::fs_node_init_anon(current_task, &node, name)
.expect("Private anon_inode creation cannot fail");
FileObject::new_anonymous(locked, current_task, ops, node, flags)
}
/// Returns true if the `fs_node` is `Anon` and private to the `Kernel`/`FileSystem`, in which
/// case it should not have access checks applied by the LSM layer.
/// This may become part of `FsNodeOps` in future, if other private node use-cases are found.
pub fn is_private(fs_node: &FsNode) -> bool {
fs_node.downcast_ops::<Anon>().map(|anon| anon.is_private).unwrap_or(false)
}
}
struct AnonFs;
impl FileSystemOps for AnonFs {
fn statfs(
&self,
_locked: &mut Locked<FileOpsCore>,
_fs: &FileSystem,
_current_task: &CurrentTask,
) -> Result<statfs, Errno> {
Ok(default_statfs(ANON_INODE_FS_MAGIC))
}
fn name(&self) -> &'static FsStr {
"anon_inodefs".into()
}
}
pub fn anon_fs<L>(locked: &mut Locked<L>, kernel: &Kernel) -> FileSystemHandle
where
L: LockEqualOrBefore<FileOpsCore>,
{
struct AnonFsHandle(FileSystemHandle);
kernel
.expando
.get_or_init(|| {
let fs = FileSystem::new(
locked,
kernel,
CacheMode::Uncached,
AnonFs,
FileSystemOptions::default(),
)
.expect("anonfs constructed with valid options");
AnonFsHandle(fs)
})
.0
.clone()
}