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    AppendLockGuard, FileObject, FileOps, FsNode, FsNodeOps, fileops_impl_seekable,
9    fs_node_impl_not_dir,
10};
11
12use crate::vfs::fileops_impl_noop_sync;
13use starnix_sync::{FileOpsCore, Locked};
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(&mut Locked<FileOpsCore>, &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(&mut Locked<FileOpsCore>, &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(&mut Locked<FileOpsCore>, &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        locked: &mut Locked<FileOpsCore>,
50        _node: &FsNode,
51        current_task: &CurrentTask,
52        _flags: OpenFlags,
53    ) -> Result<Box<dyn FileOps>, Errno> {
54        Ok(Box::new((self.create_file_ops)(locked, current_task)?))
55    }
56
57    fn truncate(
58        &self,
59        _locked: &mut Locked<FileOpsCore>,
60        _guard: &AppendLockGuard<'_>,
61        _node: &FsNode,
62        _current_task: &CurrentTask,
63        _length: u64,
64    ) -> Result<(), Errno> {
65        // TODO(tbodt): Is this right? This is the minimum to handle O_TRUNC
66        Ok(())
67    }
68}
69
70pub fn parse_unsigned_file<T: Into<u64> + std::str::FromStr>(buf: &[u8]) -> Result<T, Errno> {
71    let i = buf.iter().position(|c| !char::from(*c).is_ascii_digit()).unwrap_or(buf.len());
72    std::str::from_utf8(&buf[..i]).unwrap().parse::<T>().map_err(|_| errno!(EINVAL))
73}
74
75pub fn parse_i32_file(buf: &[u8]) -> Result<i32, Errno> {
76    let i = buf
77        .iter()
78        .position(|c| {
79            let ch = char::from(*c);
80            !(ch.is_ascii_digit() || ch == '-')
81        })
82        .unwrap_or(buf.len());
83    std::str::from_utf8(&buf[..i]).unwrap().parse::<i32>().map_err(|_| errno!(EINVAL))
84}
85
86pub fn serialize_for_file<T: Display>(value: T) -> Vec<u8> {
87    let string = format!("{}\n", value);
88    string.into_bytes()
89}
90
91pub struct BytesFile<Ops>(Arc<Ops>);
92
93impl<Ops: BytesFileOps> BytesFile<Ops> {
94    pub fn new(data: Ops) -> Self {
95        Self(Arc::new(data))
96    }
97
98    pub fn new_node(data: Ops) -> impl FsNodeOps {
99        let data = Arc::new(data);
100        SimpleFileNode::new(move |_, _| Ok(BytesFile(Arc::clone(&data))))
101    }
102}
103
104// Hand-written to avoid an unnecessary `Ops: Clone` bound which the derive would emit.
105impl<Ops> std::clone::Clone for BytesFile<Ops> {
106    fn clone(&self) -> Self {
107        Self(self.0.clone())
108    }
109}
110
111impl<Ops: BytesFileOps> FileOps for BytesFile<Ops> {
112    fileops_impl_seekable!();
113    fileops_impl_noop_sync!();
114
115    fn open(
116        &self,
117        locked: &mut Locked<FileOpsCore>,
118        file: &FileObject,
119        current_task: &CurrentTask,
120    ) -> Result<(), Errno> {
121        self.0.open(locked, file, current_task)
122    }
123
124    fn read(
125        &self,
126        locked: &mut Locked<FileOpsCore>,
127        _file: &FileObject,
128        current_task: &CurrentTask,
129        offset: usize,
130        data: &mut dyn OutputBuffer,
131    ) -> Result<usize, Errno> {
132        let content = self.0.read_locked(locked, current_task)?;
133        if offset >= content.len() {
134            return Ok(0);
135        }
136        data.write(&content[offset..])
137    }
138
139    fn write(
140        &self,
141        locked: &mut Locked<FileOpsCore>,
142        _file: &FileObject,
143        current_task: &CurrentTask,
144        _offset: usize,
145        data: &mut dyn InputBuffer,
146    ) -> Result<usize, Errno> {
147        let data = data.read_all()?;
148        let len = data.len();
149        self.0.write_locked(locked, current_task, data)?;
150        Ok(len)
151    }
152}
153
154pub trait BytesFileOps: Send + Sync + AsAny + 'static {
155    fn write(&self, _current_task: &CurrentTask, _data: Vec<u8>) -> Result<(), Errno> {
156        error!(ENOSYS)
157    }
158    fn write_locked(
159        &self,
160        _locked: &mut Locked<FileOpsCore>,
161        current_task: &CurrentTask,
162        data: Vec<u8>,
163    ) -> Result<(), Errno> {
164        self.write(current_task, data)
165    }
166    fn read(&self, _current_task: &CurrentTask) -> Result<Cow<'_, [u8]>, Errno> {
167        error!(ENOSYS)
168    }
169    fn read_locked(
170        &self,
171        _locked: &mut Locked<FileOpsCore>,
172        current_task: &CurrentTask,
173    ) -> Result<Cow<'_, [u8]>, Errno> {
174        self.read(current_task)
175    }
176    fn open(
177        &self,
178        _locked: &mut Locked<FileOpsCore>,
179        _file: &FileObject,
180        _current_task: &CurrentTask,
181    ) -> Result<(), Errno> {
182        Ok(())
183    }
184}
185
186impl BytesFileOps for Vec<u8> {
187    fn read(&self, _current_task: &CurrentTask) -> Result<Cow<'_, [u8]>, Errno> {
188        Ok(self.into())
189    }
190}
191
192impl<T> BytesFileOps for T
193where
194    T: Fn() -> Result<String, Errno> + Send + Sync + 'static,
195{
196    fn read(&self, _current_task: &CurrentTask) -> Result<Cow<'_, [u8]>, Errno> {
197        let data = self()?;
198        Ok(data.into_bytes().into())
199    }
200}
201
202pub fn create_bytes_file_with_handler<F>(kernel: Weak<Kernel>, kernel_handler: F) -> impl FsNodeOps
203where
204    F: Fn(Arc<Kernel>) -> String + Send + Sync + 'static,
205{
206    BytesFile::new_node(move || {
207        if let Some(kernel) = kernel.upgrade() {
208            Ok(kernel_handler(kernel) + "\n")
209        } else {
210            error!(ENOENT)
211        }
212    })
213}