Skip to main content

starnix_modules_binderfs/
fs.rs

1// Copyright 2025 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::binder::BinderDevice;
6use crate::remote_binder::RemoteBinderDevice;
7use starnix_core::device::DeviceOps;
8use starnix_core::mm::MemoryAccessorExt;
9use starnix_core::task::{CurrentTask, Kernel};
10use starnix_core::vfs::pseudo::simple_file::BytesFile;
11use starnix_core::vfs::pseudo::vec_directory::{VecDirectory, VecDirectoryEntry};
12use starnix_core::vfs::{
13    CacheMode, DirEntry, DirectoryEntryType, FileObject, FileOps, FileSystem, FileSystemHandle,
14    FileSystemOps, FileSystemOptions, FsNode, FsNodeHandle, FsNodeInfo, FsNodeOps, FsStr, FsString,
15    NamespaceNode, SpecialNode, fileops_impl_dataless, fileops_impl_nonseekable,
16    fileops_impl_noop_sync, fs_node_impl_dir_readonly,
17};
18use starnix_sync::{BinderFsDevicesLevel, LockDepMutex};
19use starnix_syscalls::{SUCCESS, SyscallArg, SyscallResult};
20use starnix_types::vfs::default_statfs;
21use starnix_uapi::auth::FsCred;
22use starnix_uapi::device_id::DeviceId;
23use starnix_uapi::errors::{Errno, error};
24use starnix_uapi::file_mode::mode;
25use starnix_uapi::open_flags::OpenFlags;
26use starnix_uapi::user_address::{UserAddress, UserRef};
27use starnix_uapi::{BINDERFS_SUPER_MAGIC, statfs, uapi};
28use std::collections::BTreeMap;
29use std::collections::btree_map::Entry;
30use std::sync::Arc;
31
32pub struct BinderFs;
33impl FileSystemOps for BinderFs {
34    fn statfs(&self, _fs: &FileSystem, _current_task: &CurrentTask) -> Result<statfs, Errno> {
35        Ok(default_statfs(BINDERFS_SUPER_MAGIC))
36    }
37    fn name(&self) -> &'static FsStr {
38        "binder".into()
39    }
40}
41
42const DEFAULT_BINDERS: [&str; 3] = ["binder", "hwbinder", "vndbinder"];
43const FEATURES_DIR: &str = "features";
44const BINDER_CONTROL_DEVICE: &str = "binder-control";
45// Binders with these names cannot be dynamically created using binder-control.
46const RESERVED_NAMES: [&str; 2] = [FEATURES_DIR, BINDER_CONTROL_DEVICE];
47
48#[derive(Debug)]
49pub struct BinderFsDir {
50    control_device: DeviceId,
51    state: Arc<BinderFsState>,
52}
53
54#[derive(Debug)]
55pub struct BinderFsState {
56    devices: LockDepMutex<BTreeMap<FsString, DeviceId>, BinderFsDevicesLevel>,
57}
58
59impl BinderFsDir {
60    pub fn new(kernel: &Kernel) -> Result<Self, Errno> {
61        let registry = &kernel.device_registry;
62        let mut devices = BTreeMap::<FsString, DeviceId>::default();
63        let remote_device =
64            registry.register_silent_dyn_device("remote-binder".into(), RemoteBinderDevice {})?;
65        devices.insert("remote".into(), remote_device.devt);
66
67        for name in DEFAULT_BINDERS {
68            let device_metadata =
69                registry.register_silent_dyn_device(name.into(), BinderDevice::default())?;
70            devices.insert(name.into(), device_metadata.devt);
71        }
72        let state = Arc::new(BinderFsState { devices: devices.into() });
73
74        let control_device = registry
75            .register_silent_dyn_device(
76                BINDER_CONTROL_DEVICE.into(),
77                BinderControlDevice { state: state.clone() },
78            )?
79            .devt;
80
81        Ok(Self { control_device, state })
82    }
83}
84
85impl FsNodeOps for BinderFsDir {
86    fs_node_impl_dir_readonly!();
87
88    fn create_file_ops(
89        &self,
90        _node: &FsNode,
91        _current_task: &CurrentTask,
92        _flags: OpenFlags,
93    ) -> Result<Box<dyn FileOps>, Errno> {
94        let mut entries = self
95            .state
96            .devices
97            .lock()
98            .keys()
99            .map(|name| VecDirectoryEntry {
100                entry_type: DirectoryEntryType::CHR,
101                name: name.clone(),
102                inode: None,
103            })
104            .collect::<Vec<_>>();
105        entries.push(VecDirectoryEntry {
106            entry_type: DirectoryEntryType::DIR,
107            name: FEATURES_DIR.into(),
108            inode: None,
109        });
110        entries.push(VecDirectoryEntry {
111            entry_type: DirectoryEntryType::CHR,
112            name: BINDER_CONTROL_DEVICE.into(),
113            inode: None,
114        });
115        Ok(VecDirectory::new_file(entries))
116    }
117
118    fn lookup(
119        &self,
120        node: &FsNode,
121        _current_task: &CurrentTask,
122        name: &FsStr,
123    ) -> Result<FsNodeHandle, Errno> {
124        if name == FEATURES_DIR {
125            Ok(node.fs().create_node_and_allocate_node_id(
126                BinderFeaturesDir::new(),
127                FsNodeInfo::new(mode!(IFDIR, 0o755), FsCred::root()),
128            ))
129        } else if name == BINDER_CONTROL_DEVICE {
130            let mut info = FsNodeInfo::new(mode!(IFCHR, 0o600), FsCred::root());
131            info.rdev = self.control_device;
132            Ok(node.fs().create_node_and_allocate_node_id(SpecialNode, info))
133        } else if let Some(dev) = self.state.devices.lock().get(name) {
134            let mode = if name == "remote" { mode!(IFCHR, 0o444) } else { mode!(IFCHR, 0o600) };
135            let mut info = FsNodeInfo::new(mode, FsCred::root());
136            info.rdev = *dev;
137            Ok(node.fs().create_node_and_allocate_node_id(SpecialNode, info))
138        } else {
139            error!(ENOENT, format!("looking for {name}"))
140        }
141    }
142}
143
144impl BinderFs {
145    pub fn new_fs(
146        current_task: &CurrentTask,
147        options: FileSystemOptions,
148    ) -> Result<FileSystemHandle, Errno> {
149        let kernel = current_task.kernel();
150        let fs = FileSystem::new(kernel, CacheMode::Permanent, BinderFs, options)?;
151        let ops = BinderFsDir::new(kernel)?;
152        let root_ino = fs.allocate_ino();
153        fs.create_root(root_ino, ops);
154        Ok(fs)
155    }
156}
157
158#[derive(Clone)]
159struct BinderControlDevice {
160    state: Arc<BinderFsState>,
161}
162
163impl DeviceOps for BinderControlDevice {
164    fn open(
165        &self,
166        _current_task: &CurrentTask,
167        _devt: DeviceId,
168        _node: &NamespaceNode,
169        _flags: OpenFlags,
170    ) -> Result<Box<dyn FileOps>, Errno> {
171        Ok(Box::new(self.clone()))
172    }
173}
174
175impl FileOps for BinderControlDevice {
176    fileops_impl_dataless!();
177    fileops_impl_nonseekable!();
178    fileops_impl_noop_sync!();
179
180    fn ioctl(
181        &self,
182        _file: &FileObject,
183        current_task: &CurrentTask,
184        request: u32,
185        arg: SyscallArg,
186    ) -> Result<SyscallResult, Errno> {
187        if request == uapi::BINDER_CTL_ADD {
188            let user_arg = UserAddress::from(arg);
189            if user_arg.is_null() {
190                return error!(EINVAL);
191            }
192            let user_ref = UserRef::<uapi::binderfs_device>::new(user_arg);
193            let mut request = current_task.read_object(user_ref)?;
194            let name: Vec<u8> =
195                request.name.iter().copied().map(|x| x as u8).take_while(|x| *x != 0).collect();
196            // Invalid names return EACCES.
197            if DirEntry::is_reserved_name((*name).into()) {
198                return error!(EACCES);
199            }
200            if name.contains(&('/' as u8)) {
201                return error!(EACCES);
202            }
203            // Names of already-existing objects return EEXIST.
204            for reserved_name in RESERVED_NAMES {
205                if *name == *reserved_name.as_bytes() {
206                    return error!(EEXIST);
207                }
208            }
209            let mut devices = self.state.devices.lock();
210            match devices.entry(FsString::from(name.clone())) {
211                Entry::Occupied(_) => error!(EEXIST),
212                Entry::Vacant(entry) => {
213                    let kernel = current_task.kernel();
214                    let device_metadata = kernel
215                        .device_registry
216                        .register_silent_dyn_device((*name).into(), BinderDevice::default())?;
217                    entry.insert(device_metadata.devt);
218                    request.major = device_metadata.devt.major();
219                    request.minor = device_metadata.devt.minor();
220                    current_task.write_object(user_ref, &request)?;
221                    Ok(SUCCESS)
222                }
223            }
224        } else {
225            error!(EINVAL)
226        }
227    }
228}
229
230struct BinderFeaturesDir {
231    features: BTreeMap<FsString, bool>,
232}
233
234impl BinderFeaturesDir {
235    fn new() -> Self {
236        Self { features: BTreeMap::from([("freeze_notification".into(), true)]) }
237    }
238}
239
240impl FsNodeOps for BinderFeaturesDir {
241    fs_node_impl_dir_readonly!();
242
243    fn create_file_ops(
244        &self,
245        _node: &FsNode,
246        _current_task: &CurrentTask,
247        _flags: OpenFlags,
248    ) -> Result<Box<dyn FileOps>, Errno> {
249        let entries = self
250            .features
251            .keys()
252            .map(|name| VecDirectoryEntry {
253                entry_type: DirectoryEntryType::REG,
254                name: name.clone(),
255                inode: None,
256            })
257            .collect::<Vec<_>>();
258        Ok(VecDirectory::new_file(entries))
259    }
260
261    fn lookup(
262        &self,
263        node: &FsNode,
264        _current_task: &CurrentTask,
265        name: &FsStr,
266    ) -> Result<FsNodeHandle, Errno> {
267        if let Some(enable) = self.features.get(name) {
268            return Ok(node.fs().create_node_and_allocate_node_id(
269                BytesFile::new_node(if *enable { b"1\n" } else { b"0\n" }.to_vec()),
270                FsNodeInfo::new(mode!(IFREG, 0o444), FsCred::root()),
271            ));
272        }
273        error!(ENOENT, format!("looking for {name}"))
274    }
275}