Skip to main content

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::task::{CurrentTask, Kernel};
6use crate::vfs::buffers::{InputBuffer, OutputBuffer};
7use crate::vfs::{
8    AppendLockWriteGuard, FileObject, FileOps, FsNode, FsNodeOps, fileops_impl_seekable,
9    fs_node_impl_not_dir,
10};
11
12use crate::vfs::fileops_impl_noop_sync;
13
14use starnix_uapi::as_any::AsAny;
15use starnix_uapi::errors::Errno;
16use starnix_uapi::open_flags::OpenFlags;
17use starnix_uapi::{errno, error};
18use std::borrow::Cow;
19use std::fmt::Display;
20use std::sync::{Arc, Weak};
21
22pub struct SimpleFileNode<F, O>
23where
24    F: Fn(&CurrentTask) -> Result<O, Errno>,
25    O: FileOps,
26{
27    create_file_ops: F,
28}
29
30impl<F, O> SimpleFileNode<F, O>
31where
32    F: Fn(&CurrentTask) -> Result<O, Errno> + Send + Sync + 'static,
33    O: FileOps,
34{
35    pub fn new(create_file_ops: F) -> Self {
36        Self { create_file_ops }
37    }
38}
39
40impl<F, O> FsNodeOps for SimpleFileNode<F, O>
41where
42    F: Fn(&CurrentTask) -> Result<O, Errno> + Send + Sync + 'static,
43    O: FileOps,
44{
45    fs_node_impl_not_dir!();
46
47    fn create_file_ops(
48        &self,
49        _node: &FsNode,
50        current_task: &CurrentTask,
51        _flags: OpenFlags,
52    ) -> Result<Box<dyn FileOps>, Errno> {
53        Ok(Box::new((self.create_file_ops)(current_task)?))
54    }
55
56    fn truncate(
57        &self,
58        _guard: &AppendLockWriteGuard<'_>,
59        _node: &FsNode,
60        _current_task: &CurrentTask,
61        _length: u64,
62    ) -> Result<(), Errno> {
63        // TODO(tbodt): Is this right? This is the minimum to handle O_TRUNC
64        Ok(())
65    }
66}
67
68pub fn parse_unsigned_file<T: Into<u64> + std::str::FromStr>(buf: &[u8]) -> Result<T, Errno> {
69    let i = buf.iter().position(|c| !char::from(*c).is_ascii_digit()).unwrap_or(buf.len());
70    std::str::from_utf8(&buf[..i]).unwrap().parse::<T>().map_err(|_| errno!(EINVAL))
71}
72
73pub fn parse_i32_file(buf: &[u8]) -> Result<i32, Errno> {
74    let i = buf
75        .iter()
76        .position(|c| {
77            let ch = char::from(*c);
78            !(ch.is_ascii_digit() || ch == '-')
79        })
80        .unwrap_or(buf.len());
81    std::str::from_utf8(&buf[..i]).unwrap().parse::<i32>().map_err(|_| errno!(EINVAL))
82}
83
84pub fn serialize_for_file<T: Display>(value: T) -> Vec<u8> {
85    let string = format!("{}\n", value);
86    string.into_bytes()
87}
88
89pub struct BytesFile<Ops>(Arc<Ops>);
90
91impl<Ops: BytesFileOps> BytesFile<Ops> {
92    pub fn new(data: Ops) -> Self {
93        Self(Arc::new(data))
94    }
95
96    pub fn new_node(data: Ops) -> impl FsNodeOps {
97        let data = Arc::new(data);
98        SimpleFileNode::new(move |_| Ok(BytesFile(Arc::clone(&data))))
99    }
100}
101
102// Hand-written to avoid an unnecessary `Ops: Clone` bound which the derive would emit.
103impl<Ops> std::clone::Clone for BytesFile<Ops> {
104    fn clone(&self) -> Self {
105        Self(self.0.clone())
106    }
107}
108
109impl<Ops: BytesFileOps> FileOps for BytesFile<Ops> {
110    fileops_impl_seekable!();
111    fileops_impl_noop_sync!();
112
113    fn open(&self, file: &FileObject, current_task: &CurrentTask) -> Result<(), Errno> {
114        self.0.open(file, current_task)
115    }
116
117    fn read(
118        &self,
119        _file: &FileObject,
120        current_task: &CurrentTask,
121        offset: usize,
122        data: &mut dyn OutputBuffer,
123    ) -> Result<usize, Errno> {
124        let content = self.0.read(current_task)?;
125        if offset >= content.len() {
126            return Ok(0);
127        }
128        data.write(&content[offset..])
129    }
130
131    fn write(
132        &self,
133        _file: &FileObject,
134        current_task: &CurrentTask,
135        _offset: usize,
136        data: &mut dyn InputBuffer,
137    ) -> Result<usize, Errno> {
138        let data = data.read_all()?;
139        let len = data.len();
140        self.0.write(current_task, data)?;
141        Ok(len)
142    }
143}
144
145pub trait BytesFileOps: Send + Sync + AsAny + 'static {
146    fn write(&self, _current_task: &CurrentTask, _data: Vec<u8>) -> Result<(), Errno> {
147        error!(ENOSYS)
148    }
149    fn read(&self, _current_task: &CurrentTask) -> Result<Cow<'_, [u8]>, Errno> {
150        error!(ENOSYS)
151    }
152    fn open(&self, _file: &FileObject, _current_task: &CurrentTask) -> Result<(), Errno> {
153        Ok(())
154    }
155}
156
157impl BytesFileOps for Vec<u8> {
158    fn read(&self, _current_task: &CurrentTask) -> Result<Cow<'_, [u8]>, Errno> {
159        Ok(self.into())
160    }
161}
162
163impl<T> BytesFileOps for T
164where
165    T: Fn() -> Result<String, Errno> + Send + Sync + 'static,
166{
167    fn read(&self, _current_task: &CurrentTask) -> Result<Cow<'_, [u8]>, Errno> {
168        let data = self()?;
169        Ok(data.into_bytes().into())
170    }
171}
172
173pub fn create_bytes_file_with_handler<F>(kernel: Weak<Kernel>, kernel_handler: F) -> impl FsNodeOps
174where
175    F: Fn(Arc<Kernel>) -> String + Send + Sync + 'static,
176{
177    BytesFile::new_node(move || {
178        if let Some(kernel) = kernel.upgrade() {
179            Ok(kernel_handler(kernel) + "\n")
180        } else {
181            error!(ENOENT)
182        }
183    })
184}