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