starnix_core/vfs/pseudo/
simple_file.rs

1// Copyright 2021 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::security;
6use crate::task::{CurrentTask, Kernel};
7use crate::vfs::buffers::{InputBuffer, OutputBuffer};
8use crate::vfs::{
9    AppendLockGuard, CheckAccessReason, FileObject, FileOps, FsNode, FsNodeInfo, FsNodeOps,
10    fileops_impl_seekable, fs_node_impl_not_dir,
11};
12
13use crate::vfs::fileops_impl_noop_sync;
14use starnix_sync::{FileOpsCore, Locked, RwLock};
15use starnix_uapi::as_any::AsAny;
16use starnix_uapi::auth::Capabilities;
17use starnix_uapi::errors::Errno;
18use starnix_uapi::open_flags::OpenFlags;
19use starnix_uapi::{errno, error};
20use std::borrow::Cow;
21use std::fmt::Display;
22use std::sync::{Arc, Weak};
23
24pub struct SimpleFileNode<F, O>
25where
26    F: Fn() -> Result<O, Errno>,
27    O: FileOps,
28{
29    create_file_ops: F,
30
31    /// Capabilities that should cause `check_access` to always pass.
32    capabilities: Capabilities,
33}
34
35impl<F, O> SimpleFileNode<F, O>
36where
37    F: Fn() -> Result<O, Errno> + Send + Sync,
38    O: FileOps,
39{
40    pub fn new(create_file_ops: F) -> SimpleFileNode<F, O> {
41        SimpleFileNode { create_file_ops, capabilities: Capabilities::empty() }
42    }
43
44    pub fn new_with_capabilities(
45        create_file_ops: F,
46        capabilities: Capabilities,
47    ) -> SimpleFileNode<F, O> {
48        SimpleFileNode { create_file_ops, capabilities }
49    }
50}
51
52impl<F, O> FsNodeOps for SimpleFileNode<F, O>
53where
54    F: Fn() -> Result<O, Errno> + Send + Sync + 'static,
55    O: FileOps,
56{
57    fs_node_impl_not_dir!();
58
59    fn check_access(
60        &self,
61        _locked: &mut Locked<FileOpsCore>,
62        node: &FsNode,
63        current_task: &CurrentTask,
64        permission_flags: security::PermissionFlags,
65        info: &RwLock<FsNodeInfo>,
66        reason: CheckAccessReason,
67        audit_context: security::Auditable<'_>,
68    ) -> Result<(), Errno> {
69        if self.capabilities != Capabilities::empty()
70            && security::is_task_capable_noaudit(current_task, self.capabilities)
71        {
72            Ok(())
73        } else {
74            node.default_check_access_impl(
75                current_task,
76                permission_flags,
77                reason,
78                info.read(),
79                audit_context,
80            )
81        }
82    }
83
84    fn create_file_ops(
85        &self,
86        _locked: &mut Locked<FileOpsCore>,
87        _node: &FsNode,
88        _current_task: &CurrentTask,
89        _flags: OpenFlags,
90    ) -> Result<Box<dyn FileOps>, Errno> {
91        Ok(Box::new((self.create_file_ops)()?))
92    }
93
94    fn truncate(
95        &self,
96        _locked: &mut Locked<FileOpsCore>,
97        _guard: &AppendLockGuard<'_>,
98        _node: &FsNode,
99        _current_task: &CurrentTask,
100        _length: u64,
101    ) -> Result<(), Errno> {
102        // TODO(tbodt): Is this right? This is the minimum to handle O_TRUNC
103        Ok(())
104    }
105}
106
107pub fn parse_unsigned_file<T: Into<u64> + std::str::FromStr>(buf: &[u8]) -> Result<T, Errno> {
108    let i = buf.iter().position(|c| !char::from(*c).is_ascii_digit()).unwrap_or(buf.len());
109    std::str::from_utf8(&buf[..i]).unwrap().parse::<T>().map_err(|_| errno!(EINVAL))
110}
111
112pub fn parse_i32_file(buf: &[u8]) -> Result<i32, Errno> {
113    let i = buf
114        .iter()
115        .position(|c| {
116            let ch = char::from(*c);
117            !(ch.is_ascii_digit() || ch == '-')
118        })
119        .unwrap_or(buf.len());
120    std::str::from_utf8(&buf[..i]).unwrap().parse::<i32>().map_err(|_| errno!(EINVAL))
121}
122
123pub fn serialize_for_file<T: Display>(value: T) -> Vec<u8> {
124    let string = format!("{}\n", value);
125    string.into_bytes()
126}
127
128pub struct BytesFile<Ops>(Arc<Ops>);
129
130impl<Ops: BytesFileOps> BytesFile<Ops> {
131    pub fn new(data: Ops) -> Self {
132        Self(Arc::new(data))
133    }
134
135    pub fn new_node(data: Ops) -> impl FsNodeOps {
136        let data = Arc::new(data);
137        SimpleFileNode::new(move || Ok(BytesFile(Arc::clone(&data))))
138    }
139}
140
141// Hand-written to avoid an unnecessary `Ops: Clone` bound which the derive would emit.
142impl<Ops> std::clone::Clone for BytesFile<Ops> {
143    fn clone(&self) -> Self {
144        Self(self.0.clone())
145    }
146}
147
148impl<Ops: BytesFileOps> FileOps for BytesFile<Ops> {
149    fileops_impl_seekable!();
150    fileops_impl_noop_sync!();
151
152    fn open(
153        &self,
154        locked: &mut Locked<FileOpsCore>,
155        file: &FileObject,
156        current_task: &CurrentTask,
157    ) -> Result<(), Errno> {
158        self.0.open(locked, file, current_task)
159    }
160
161    fn read(
162        &self,
163        locked: &mut Locked<FileOpsCore>,
164        _file: &FileObject,
165        current_task: &CurrentTask,
166        offset: usize,
167        data: &mut dyn OutputBuffer,
168    ) -> Result<usize, Errno> {
169        let content = self.0.read_locked(locked, current_task)?;
170        if offset >= content.len() {
171            return Ok(0);
172        }
173        data.write(&content[offset..])
174    }
175
176    fn write(
177        &self,
178        locked: &mut Locked<FileOpsCore>,
179        _file: &FileObject,
180        current_task: &CurrentTask,
181        _offset: usize,
182        data: &mut dyn InputBuffer,
183    ) -> Result<usize, Errno> {
184        let data = data.read_all()?;
185        let len = data.len();
186        self.0.write_locked(locked, current_task, data)?;
187        Ok(len)
188    }
189}
190
191pub trait BytesFileOps: Send + Sync + AsAny + 'static {
192    fn write(&self, _current_task: &CurrentTask, _data: Vec<u8>) -> Result<(), Errno> {
193        error!(ENOSYS)
194    }
195    fn write_locked(
196        &self,
197        _locked: &mut Locked<FileOpsCore>,
198        current_task: &CurrentTask,
199        data: Vec<u8>,
200    ) -> Result<(), Errno> {
201        self.write(current_task, data)
202    }
203    fn read(&self, _current_task: &CurrentTask) -> Result<Cow<'_, [u8]>, Errno> {
204        error!(ENOSYS)
205    }
206    fn read_locked(
207        &self,
208        _locked: &mut Locked<FileOpsCore>,
209        current_task: &CurrentTask,
210    ) -> Result<Cow<'_, [u8]>, Errno> {
211        self.read(current_task)
212    }
213    fn open(
214        &self,
215        _locked: &mut Locked<FileOpsCore>,
216        _file: &FileObject,
217        _current_task: &CurrentTask,
218    ) -> Result<(), Errno> {
219        Ok(())
220    }
221}
222
223impl BytesFileOps for Vec<u8> {
224    fn read(&self, _current_task: &CurrentTask) -> Result<Cow<'_, [u8]>, Errno> {
225        Ok(self.into())
226    }
227}
228
229impl<T> BytesFileOps for T
230where
231    T: Fn() -> Result<String, Errno> + Send + Sync + 'static,
232{
233    fn read(&self, _current_task: &CurrentTask) -> Result<Cow<'_, [u8]>, Errno> {
234        let data = self()?;
235        Ok(data.into_bytes().into())
236    }
237}
238
239pub fn create_bytes_file_with_handler<F>(kernel: Weak<Kernel>, kernel_handler: F) -> impl FsNodeOps
240where
241    F: Fn(Arc<Kernel>) -> String + Send + Sync + 'static,
242{
243    BytesFile::new_node(move || {
244        if let Some(kernel) = kernel.upgrade() {
245            Ok(kernel_handler(kernel) + "\n")
246        } else {
247            error!(ENOENT)
248        }
249    })
250}