Skip to main content

starnix_core/vfs/pseudo/
dynamic_file.rs

1// Copyright 2023 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;
6use crate::vfs::buffers::{InputBuffer, OutputBuffer};
7use crate::vfs::pseudo::simple_file::SimpleFileNode;
8use crate::vfs::{
9    Buffer, FileObject, FileOps, FsNodeOps, OutputBufferCallback, PeekBufferSegmentsCallback,
10    SeekTarget, default_seek, fileops_impl_noop_sync,
11};
12use starnix_sync::{DynamicFileStateLock, LockDepMutex};
13use starnix_uapi::errors::Errno;
14use starnix_uapi::{errno, error, off_t};
15use std::collections::VecDeque;
16
17unsafe extern "C" {
18    // Declare a symbol that doesn't exist. If the compiler cannot prove that this is never used,
19    // this will create a compilation error showing an issue with the usage of the traits in this
20    // file.
21    fn undefined_symbol_to_prevent_compilation();
22}
23
24pub trait SequenceFileSource: Send + Sync + 'static {
25    type Cursor: Default + Send;
26    fn next(
27        &self,
28        _current_task: &CurrentTask,
29        _cursor: Self::Cursor,
30        _sink: &mut DynamicFileBuf,
31    ) -> Result<Option<Self::Cursor>, Errno> {
32        // SAFETY: This cannot compile and ensure this method is never reached
33        unsafe {
34            undefined_symbol_to_prevent_compilation();
35        }
36        panic!("Either next or next_locked must be implemented");
37    }
38    fn next_locked(
39        &self,
40        current_task: &CurrentTask,
41        cursor: Self::Cursor,
42        sink: &mut DynamicFileBuf,
43    ) -> Result<Option<Self::Cursor>, Errno> {
44        self.next(current_task, cursor, sink)
45    }
46    fn write(
47        &self,
48        _current_task: &CurrentTask,
49        _offset: usize,
50        _data: &mut dyn InputBuffer,
51    ) -> Result<usize, Errno> {
52        error!(ENOSYS)
53    }
54}
55
56pub trait DynamicFileSource: Send + Sync + 'static {
57    fn generate(
58        &self,
59        _current_task: &CurrentTask,
60        _sink: &mut DynamicFileBuf,
61    ) -> Result<(), Errno> {
62        // SAFETY: This cannot compile and ensure this method is never reached
63        unsafe {
64            undefined_symbol_to_prevent_compilation();
65        }
66        panic!("Either generate or generate_locked must be implemented");
67    }
68    fn generate_locked(
69        &self,
70        current_task: &CurrentTask,
71        sink: &mut DynamicFileBuf,
72    ) -> Result<(), Errno> {
73        self.generate(current_task, sink)
74    }
75    fn write(
76        &self,
77        _current_task: &CurrentTask,
78        _offset: usize,
79        _data: &mut dyn InputBuffer,
80    ) -> Result<usize, Errno> {
81        error!(ENOSYS)
82    }
83}
84
85impl<T> SequenceFileSource for T
86where
87    T: DynamicFileSource,
88{
89    type Cursor = ();
90    fn next_locked(
91        &self,
92        current_task: &CurrentTask,
93        _cursor: (),
94        sink: &mut DynamicFileBuf,
95    ) -> Result<Option<()>, Errno> {
96        self.generate_locked(current_task, sink).map(|_| None)
97    }
98    fn write(
99        &self,
100        current_task: &CurrentTask,
101        offset: usize,
102        data: &mut dyn InputBuffer,
103    ) -> Result<usize, Errno> {
104        DynamicFileSource::write(self, current_task, offset, data)
105    }
106}
107
108/// `DynamicFile` implements `FileOps` for files whose contents are generated by the kernel
109/// dynamically either from a sequence (see `SequenceFileSource`) or as a single blob of data
110/// (see `DynamicFileSource`). The file may be updated dynamically as it's normally expected
111/// for files in `/proc`, e.g. when seeking back from the current position.
112///
113/// The following example shows how `DynamicFile` can be used with a `DynamicFileSource`:
114/// ```
115/// #[derive(Clone)]
116/// pub struct SimpleFile(u32);
117/// impl SimpleFile {
118///     pub fn new_node(param: u32) -> impl FsNodeOps {
119///         DynamicFile::new_node(Self(param))
120///     }
121/// }
122/// impl DynamicFileSource for SimpleFile {
123///     fn generate(&self, sink: &mut DynamicFileBuf) -> Result<(), Errno> {
124///         writeln!(sink, "param: {}", self.0)?
125///         Ok(())
126///     }
127/// }
128/// ```
129///
130/// `SequenceFileSource` should be used to generate file contents from a sequence of objects.
131/// `SequenceFileSource::next()` takes the cursor for the current position, outputs the next
132/// chunk of data in the sequence, and returns the the advanced cursor value. At the start of
133/// iteration, the cursor is `Default::default()`. The end of the sequence is indicated by
134/// returning `None`.
135///
136/// The next example generates the contents from a sequence of integer values:
137/// ```
138/// [#derive(Clone)]
139/// struct IntegersFile;
140/// impl SequenceFileSource for IntegersFile {
141///     type Cursor = usize;
142///     fn next(&self, cursor: usize, sink: &mut DynamicFileBuf) -> Result<Option<usize>, Errno> {
143///         // The cursor starts at i32::default(), which is 0.
144///         writeln!(sink, "{}", cursor)?;
145///         if cursor > 1000 {
146///             // End of the sequence.
147///             return Ok(None);
148///         }
149///         Ok(Some(cursor + 1))
150///     }
151/// }
152/// ```
153///
154/// Writable files should implement the write method as shown in the example below:
155/// ```
156/// struct WritableProcFileSource {
157///   data: usize,
158/// }
159/// impl DynamicFileSource for WritableProcFileSource {
160///     fn generate(&self, sink: &mut DynamicFileBuf) -> Result<(), Errno> {
161///         writeln!("{}", self.data);
162///         Ok(())
163///     }
164///     fn write(
165///         &self,
166///         _current_task: &CurrentTask,
167///         _offset: usize,
168///         data: &mut dyn InputBuffer,
169///     ) -> Result<usize, Errno> {
170///         ... Process write() ...
171///     }
172/// }
173/// impl WritableProcFile {
174///     fn new() -> DynamicFile {
175///         DynamicFile::new(WritableProcFileSource { data: 42 })
176///     }
177/// }
178/// ```
179///
180pub struct DynamicFile<Source: SequenceFileSource> {
181    state: LockDepMutex<DynamicFileState<Source>, DynamicFileStateLock>,
182}
183
184impl<Source: SequenceFileSource> DynamicFile<Source> {
185    pub fn new(source: Source) -> Self {
186        DynamicFile { state: DynamicFileState::new(source).into() }
187    }
188}
189
190impl<Source: SequenceFileSource + Clone> DynamicFile<Source> {
191    pub fn new_node(source: Source) -> impl FsNodeOps {
192        SimpleFileNode::new(move |_| Ok(DynamicFile::new(source.clone())))
193    }
194}
195
196impl<Source: SequenceFileSource> DynamicFile<Source> {
197    fn read_internal(
198        &self,
199        current_task: &CurrentTask,
200        offset: usize,
201        data: &mut dyn OutputBuffer,
202    ) -> Result<usize, Errno> {
203        self.state.lock().read(current_task, offset, data)
204    }
205    fn write_internal(
206        &self,
207        current_task: &CurrentTask,
208        offset: usize,
209        data: &mut dyn InputBuffer,
210    ) -> Result<usize, Errno> {
211        self.state.lock().write(current_task, offset, data)
212    }
213}
214
215impl<Source: SequenceFileSource> FileOps for DynamicFile<Source> {
216    fileops_impl_noop_sync!();
217
218    fn is_seekable(&self) -> bool {
219        true
220    }
221
222    fn read(
223        &self,
224        _file: &FileObject,
225        current_task: &CurrentTask,
226        offset: usize,
227        data: &mut dyn OutputBuffer,
228    ) -> Result<usize, Errno> {
229        self.read_internal(current_task, offset, data)
230    }
231
232    fn write(
233        &self,
234        _file: &FileObject,
235        current_task: &CurrentTask,
236        offset: usize,
237        data: &mut dyn InputBuffer,
238    ) -> Result<usize, Errno> {
239        self.write_internal(current_task, offset, data)
240    }
241
242    fn seek(
243        &self,
244        _file: &FileObject,
245        _current_task: &CurrentTask,
246        current_offset: off_t,
247        target: SeekTarget,
248    ) -> Result<off_t, Errno> {
249        default_seek(current_offset, target, || error!(EINVAL))
250    }
251}
252
253/// Internal state of a `DynamicFile`.
254struct DynamicFileState<Source: SequenceFileSource> {
255    /// The `Source` that's used to generate content of the file.
256    source: Source,
257
258    /// The current position in the sequence. This is an opaque object. Stepping the iterator
259    /// replaces it with the next value in the sequence.
260    cursor: Option<Source::Cursor>,
261
262    /// Buffer for upcoming data in the sequence. Read calls will expand this buffer until it is
263    /// big enough and then copy out data from it.
264    buf: DynamicFileBuf,
265
266    /// The current seek offset in the file. The first byte in the buffer is at this offset in the
267    /// file.
268    ///
269    /// If a read has an offset greater than this, bytes will be generated from the iterator
270    /// and skipped. If a read has an offset less than this, all state is reset and iteration
271    /// starts from the beginning until it reaches the requested offset.
272    byte_offset: usize,
273}
274
275impl<Source: SequenceFileSource> DynamicFileState<Source> {
276    fn new(source: Source) -> Self {
277        Self {
278            source,
279            cursor: Some(Source::Cursor::default()),
280            buf: DynamicFileBuf::default(),
281            byte_offset: 0,
282        }
283    }
284}
285
286impl<Source: SequenceFileSource> DynamicFileState<Source> {
287    fn reset(&mut self) {
288        self.cursor = Some(Source::Cursor::default());
289        self.buf = DynamicFileBuf::default();
290        self.byte_offset = 0;
291    }
292
293    fn read(
294        &mut self,
295        current_task: &CurrentTask,
296        offset: usize,
297        data: &mut dyn OutputBuffer,
298    ) -> Result<usize, Errno> {
299        if offset != self.byte_offset {
300            self.reset();
301        }
302        let read_size = data.available();
303
304        // 1. Grow the buffer until either EOF or it's at least as big as the read request
305        while self.byte_offset + self.buf.0.len() < offset + read_size {
306            let cursor = if let Some(cursor) = std::mem::take(&mut self.cursor) {
307                cursor
308            } else {
309                break;
310            };
311            let mut buf = std::mem::take(&mut self.buf);
312            self.cursor = self.source.next_locked(current_task, cursor, &mut buf).map_err(|e| {
313                // Reset everything on failure
314                self.reset();
315                e
316            })?;
317            self.buf = buf;
318
319            // If the seek pointer is ahead of our current byte offset, we will generate data that
320            // needs to be thrown away. Calculation for that is here.
321            let to_drain = std::cmp::min(offset - self.byte_offset, self.buf.0.len());
322            self.buf.0.drain(..to_drain);
323            self.byte_offset += to_drain;
324        }
325
326        // 2. Copy out as much of the data as possible. `write()` may need to be called twice
327        // because `VecDeque` keeps the data in a ring buffer.
328        let (slice1, slice2) = self.buf.0.as_slices();
329        let mut written = data.write(slice1)?;
330        if written == slice1.len() && !slice2.is_empty() {
331            written += data.write(slice2)?;
332        }
333
334        // 3. Move the current position and drop the consumed data.
335        self.buf.0.drain(..written);
336        self.byte_offset += written;
337        Ok(written)
338    }
339
340    fn write(
341        &mut self,
342        current_task: &CurrentTask,
343        offset: usize,
344        data: &mut dyn InputBuffer,
345    ) -> Result<usize, Errno> {
346        self.source.write(current_task, offset, data)
347    }
348}
349
350#[derive(Debug, Default)]
351pub struct DynamicFileBuf(VecDeque<u8>);
352impl DynamicFileBuf {
353    pub fn write(&mut self, data: &[u8]) {
354        self.0.extend(data);
355    }
356    pub fn write_iter<I>(&mut self, data: I)
357    where
358        I: IntoIterator<Item = u8>,
359    {
360        self.0.extend(data);
361    }
362    pub fn write_fmt(&mut self, args: std::fmt::Arguments<'_>) -> Result<usize, Errno> {
363        let start_size = self.0.len();
364        std::io::Write::write_fmt(&mut self.0, args).map_err(|_| errno!(EINVAL))?;
365        let end_size = self.0.len();
366        Ok(end_size - start_size)
367    }
368}
369
370#[cfg(test)]
371impl From<DynamicFileBuf> for Vec<u8> {
372    fn from(buf: DynamicFileBuf) -> Self {
373        buf.0.into()
374    }
375}
376
377impl Buffer for DynamicFileBuf {
378    fn segments_count(&self) -> Result<usize, Errno> {
379        std::unimplemented!();
380    }
381
382    fn peek_each_segment(
383        &mut self,
384        _callback: &mut PeekBufferSegmentsCallback<'_>,
385    ) -> Result<(), Errno> {
386        std::unimplemented!();
387    }
388}
389
390impl OutputBuffer for DynamicFileBuf {
391    fn available(&self) -> usize {
392        std::unimplemented!();
393    }
394
395    fn bytes_written(&self) -> usize {
396        std::unimplemented!();
397    }
398
399    fn zero(&mut self) -> Result<usize, Errno> {
400        std::unimplemented!();
401    }
402
403    fn write_each(&mut self, _callback: &mut OutputBufferCallback<'_>) -> Result<usize, Errno> {
404        std::unimplemented!();
405    }
406
407    fn write_all(&mut self, buffer: &[u8]) -> Result<usize, Errno> {
408        self.write(buffer);
409        Ok(buffer.len())
410    }
411
412    unsafe fn advance(&mut self, _length: usize) -> Result<(), Errno> {
413        std::unimplemented!();
414    }
415}
416
417/// A file whose contents are fixed even if writes occur.
418pub struct ConstFile {
419    data: Vec<u8>,
420}
421
422impl DynamicFileSource for ConstFile {
423    fn generate(
424        &self,
425        _current_task: &CurrentTask,
426        sink: &mut DynamicFileBuf,
427    ) -> Result<(), Errno> {
428        sink.write(&self.data);
429        Ok(())
430    }
431
432    fn write(
433        &self,
434        _current_task: &CurrentTask,
435        _offset: usize,
436        data: &mut dyn InputBuffer,
437    ) -> Result<usize, Errno> {
438        Ok(data.drain())
439    }
440}
441
442impl ConstFile {
443    /// Create a file with the given contents.
444    pub fn new_node(data: Vec<u8>) -> impl FsNodeOps {
445        SimpleFileNode::new(move |_| Ok(DynamicFile::new(ConstFile { data: data.clone() })))
446    }
447}
448
449#[cfg(test)]
450mod tests {
451    use crate::task::CurrentTask;
452    use crate::testing::{anon_test_file, spawn_kernel_and_run};
453    use crate::vfs::pseudo::dynamic_file::{
454        DynamicFile, DynamicFileBuf, DynamicFileSource, SequenceFileSource,
455    };
456    use crate::vfs::{SeekTarget, VecOutputBuffer};
457    use starnix_sync::Mutex;
458    use starnix_uapi::errors::Errno;
459    use starnix_uapi::open_flags::OpenFlags;
460    use std::sync::Arc;
461
462    struct Counter {
463        value: Mutex<u8>,
464    }
465
466    struct TestSequenceFileSource;
467
468    impl SequenceFileSource for TestSequenceFileSource {
469        type Cursor = u8;
470        fn next(
471            &self,
472            _current_task: &CurrentTask,
473            i: u8,
474            sink: &mut DynamicFileBuf,
475        ) -> Result<Option<u8>, Errno> {
476            sink.write(&[i]);
477            Ok(if i == u8::MAX { None } else { Some(i + 1) })
478        }
479    }
480
481    #[fuchsia::test]
482    async fn test_sequence() {
483        spawn_kernel_and_run(async |current_task| {
484            let file = anon_test_file(
485                &current_task,
486                Box::new(DynamicFile::new(TestSequenceFileSource {})),
487                OpenFlags::RDONLY,
488            );
489
490            let read_at = |offset: usize, length: usize| -> Result<Vec<u8>, Errno> {
491                let mut buffer = VecOutputBuffer::new(length);
492                file.read_at(&current_task, offset, &mut buffer)?;
493                Ok(buffer.data().to_vec())
494            };
495
496            assert_eq!(read_at(0, 2).unwrap(), &[0, 1]);
497            assert_eq!(read_at(2, 2).unwrap(), &[2, 3]);
498            assert_eq!(read_at(4, 4).unwrap(), &[4, 5, 6, 7]);
499            assert_eq!(read_at(0, 2).unwrap(), &[0, 1]);
500            assert_eq!(read_at(4, 2).unwrap(), &[4, 5]);
501        })
502        .await;
503    }
504
505    struct TestFileSource {
506        counter: Arc<Counter>,
507    }
508
509    impl DynamicFileSource for TestFileSource {
510        fn generate(
511            &self,
512            _current_task: &CurrentTask,
513            sink: &mut DynamicFileBuf,
514        ) -> Result<(), Errno> {
515            let mut counter = self.counter.value.lock();
516            let base = *counter;
517            // Write 10 bytes where v[i] = base + i.
518            let data = (0..10).map(|i| base + i).collect::<Vec<u8>>();
519            sink.write(&data);
520            *counter += 1;
521            Ok(())
522        }
523    }
524
525    #[fuchsia::test]
526    async fn test_read() {
527        let counter = Arc::new(Counter { value: Mutex::new(0) });
528        spawn_kernel_and_run(async move |current_task| {
529            let file = anon_test_file(
530                &current_task,
531                Box::new(DynamicFile::new(TestFileSource { counter: counter.clone() })),
532                OpenFlags::RDONLY,
533            );
534            let read_at = |offset: usize, length: usize| -> Result<Vec<u8>, Errno> {
535                let mut buffer = VecOutputBuffer::new(length);
536                let bytes_read = file.read_at(&current_task, offset, &mut buffer)?;
537                Ok(buffer.data()[0..bytes_read].to_vec())
538            };
539
540            // Verify that we can read all data to the end.
541            assert_eq!(read_at(0, 20).unwrap(), (0..10).collect::<Vec<u8>>());
542
543            // Read from the beginning. Content should be refreshed.
544            assert_eq!(read_at(0, 2).unwrap(), [1, 2]);
545
546            // Continue reading. Content should not be updated.
547            assert_eq!(read_at(2, 2).unwrap(), [3, 4]);
548
549            // Try reading from a new position. Content should be updated.
550            assert_eq!(read_at(5, 2).unwrap(), [7, 8]);
551        })
552        .await;
553    }
554
555    #[fuchsia::test]
556    async fn test_read_and_seek() {
557        let counter = Arc::new(Counter { value: Mutex::new(0) });
558        spawn_kernel_and_run(async move |current_task| {
559            let file = anon_test_file(
560                &current_task,
561                Box::new(DynamicFile::new(TestFileSource { counter: counter.clone() })),
562                OpenFlags::RDONLY,
563            );
564            let read = |length: usize| -> Result<Vec<u8>, Errno> {
565                let mut buffer = VecOutputBuffer::new(length);
566                let bytes_read = file.read(&current_task, &mut buffer)?;
567                Ok(buffer.data()[0..bytes_read].to_vec())
568            };
569
570            // Call `read()` to read the content all the way to the end. Content should not update
571            assert_eq!(read(1).unwrap(), [0]);
572            assert_eq!(read(2).unwrap(), [1, 2]);
573            assert_eq!(read(20).unwrap(), (3..10).collect::<Vec<u8>>());
574
575            // Seek to the start of the file. Content should be updated on the following read.
576            file.seek(&current_task, SeekTarget::Set(0)).unwrap();
577            assert_eq!(*counter.value.lock(), 1);
578            assert_eq!(read(2).unwrap(), [1, 2]);
579            assert_eq!(*counter.value.lock(), 2);
580
581            // Seeking to `pos > 0` should NOT update the content immediately (lazy seek).
582            file.seek(&current_task, SeekTarget::Set(1)).unwrap();
583            assert_eq!(*counter.value.lock(), 2);
584
585            // Content should be updated on the following read.
586            assert_eq!(read(1).unwrap(), [3]);
587            assert_eq!(*counter.value.lock(), 3);
588        })
589        .await;
590    }
591}