1use 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::{FileOpsCore, Locked, Mutex};
13use starnix_uapi::errors::Errno;
14use starnix_uapi::{errno, error, off_t};
15use std::collections::VecDeque;
16
17unsafe extern "C" {
18 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 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 _locked: &mut Locked<FileOpsCore>,
41 current_task: &CurrentTask,
42 cursor: Self::Cursor,
43 sink: &mut DynamicFileBuf,
44 ) -> Result<Option<Self::Cursor>, Errno> {
45 self.next(current_task, cursor, sink)
46 }
47 fn write(
48 &self,
49 _locked: &mut Locked<FileOpsCore>,
50 _current_task: &CurrentTask,
51 _offset: usize,
52 _data: &mut dyn InputBuffer,
53 ) -> Result<usize, Errno> {
54 error!(ENOSYS)
55 }
56}
57
58pub trait DynamicFileSource: Send + Sync + 'static {
59 fn generate(
60 &self,
61 _current_task: &CurrentTask,
62 _sink: &mut DynamicFileBuf,
63 ) -> Result<(), Errno> {
64 unsafe {
66 undefined_symbol_to_prevent_compilation();
67 }
68 panic!("Either generate or generate_locked must be implemented");
69 }
70 fn generate_locked(
71 &self,
72 _locked: &mut Locked<FileOpsCore>,
73 current_task: &CurrentTask,
74 sink: &mut DynamicFileBuf,
75 ) -> Result<(), Errno> {
76 self.generate(current_task, sink)
77 }
78 fn write(
79 &self,
80 _locked: &mut Locked<FileOpsCore>,
81 _current_task: &CurrentTask,
82 _offset: usize,
83 _data: &mut dyn InputBuffer,
84 ) -> Result<usize, Errno> {
85 error!(ENOSYS)
86 }
87}
88
89impl<T> SequenceFileSource for T
90where
91 T: DynamicFileSource,
92{
93 type Cursor = ();
94 fn next_locked(
95 &self,
96 locked: &mut Locked<FileOpsCore>,
97 current_task: &CurrentTask,
98 _cursor: (),
99 sink: &mut DynamicFileBuf,
100 ) -> Result<Option<()>, Errno> {
101 self.generate_locked(locked, current_task, sink).map(|_| None)
102 }
103 fn write(
104 &self,
105 locked: &mut Locked<FileOpsCore>,
106 current_task: &CurrentTask,
107 offset: usize,
108 data: &mut dyn InputBuffer,
109 ) -> Result<usize, Errno> {
110 DynamicFileSource::write(self, locked, current_task, offset, data)
111 }
112}
113
114pub struct DynamicFile<Source: SequenceFileSource> {
188 state: Mutex<DynamicFileState<Source>>,
189}
190
191impl<Source: SequenceFileSource> DynamicFile<Source> {
192 pub fn new(source: Source) -> Self {
193 DynamicFile { state: Mutex::new(DynamicFileState::new(source)) }
194 }
195}
196
197impl<Source: SequenceFileSource + Clone> DynamicFile<Source> {
198 pub fn new_node(source: Source) -> impl FsNodeOps {
199 SimpleFileNode::new(move |_, _| Ok(DynamicFile::new(source.clone())))
200 }
201}
202
203impl<Source: SequenceFileSource> DynamicFile<Source> {
204 fn read_internal(
205 &self,
206 locked: &mut Locked<FileOpsCore>,
207 current_task: &CurrentTask,
208 offset: usize,
209 data: &mut dyn OutputBuffer,
210 ) -> Result<usize, Errno> {
211 self.state.lock().read(locked, current_task, offset, data)
212 }
213 fn write_internal(
214 &self,
215 locked: &mut Locked<FileOpsCore>,
216 current_task: &CurrentTask,
217 offset: usize,
218 data: &mut dyn InputBuffer,
219 ) -> Result<usize, Errno> {
220 self.state.lock().write(locked, current_task, offset, data)
221 }
222}
223
224impl<Source: SequenceFileSource> FileOps for DynamicFile<Source> {
225 fileops_impl_noop_sync!();
226
227 fn is_seekable(&self) -> bool {
228 true
229 }
230
231 fn read(
232 &self,
233 locked: &mut Locked<FileOpsCore>,
234 _file: &FileObject,
235 current_task: &CurrentTask,
236 offset: usize,
237 data: &mut dyn OutputBuffer,
238 ) -> Result<usize, Errno> {
239 self.read_internal(locked, current_task, offset, data)
240 }
241
242 fn write(
243 &self,
244 locked: &mut Locked<FileOpsCore>,
245 _file: &FileObject,
246 current_task: &CurrentTask,
247 offset: usize,
248 data: &mut dyn InputBuffer,
249 ) -> Result<usize, Errno> {
250 self.write_internal(locked, current_task, offset, data)
251 }
252
253 fn seek(
254 &self,
255 _locked: &mut Locked<FileOpsCore>,
256 _file: &FileObject,
257 _current_task: &CurrentTask,
258 current_offset: off_t,
259 target: SeekTarget,
260 ) -> Result<off_t, Errno> {
261 default_seek(current_offset, target, || error!(EINVAL))
262 }
263}
264
265struct DynamicFileState<Source: SequenceFileSource> {
267 source: Source,
269
270 cursor: Option<Source::Cursor>,
273
274 buf: DynamicFileBuf,
277
278 byte_offset: usize,
285}
286
287impl<Source: SequenceFileSource> DynamicFileState<Source> {
288 fn new(source: Source) -> Self {
289 Self {
290 source,
291 cursor: Some(Source::Cursor::default()),
292 buf: DynamicFileBuf::default(),
293 byte_offset: 0,
294 }
295 }
296}
297
298impl<Source: SequenceFileSource> DynamicFileState<Source> {
299 fn reset(&mut self) {
300 self.cursor = Some(Source::Cursor::default());
301 self.buf = DynamicFileBuf::default();
302 self.byte_offset = 0;
303 }
304
305 fn read(
306 &mut self,
307 locked: &mut Locked<FileOpsCore>,
308 current_task: &CurrentTask,
309 offset: usize,
310 data: &mut dyn OutputBuffer,
311 ) -> Result<usize, Errno> {
312 if offset != self.byte_offset {
313 self.reset();
314 }
315 let read_size = data.available();
316
317 while self.byte_offset + self.buf.0.len() < offset + read_size {
319 let cursor = if let Some(cursor) = std::mem::take(&mut self.cursor) {
320 cursor
321 } else {
322 break;
323 };
324 let mut buf = std::mem::take(&mut self.buf);
325 self.cursor =
326 self.source.next_locked(locked, current_task, cursor, &mut buf).map_err(|e| {
327 self.reset();
329 e
330 })?;
331 self.buf = buf;
332
333 let to_drain = std::cmp::min(offset - self.byte_offset, self.buf.0.len());
336 self.buf.0.drain(..to_drain);
337 self.byte_offset += to_drain;
338 }
339
340 let (slice1, slice2) = self.buf.0.as_slices();
343 let mut written = data.write(slice1)?;
344 if written == slice1.len() && !slice2.is_empty() {
345 written += data.write(slice2)?;
346 }
347
348 self.buf.0.drain(..written);
350 self.byte_offset += written;
351 Ok(written)
352 }
353
354 fn write(
355 &mut self,
356 locked: &mut Locked<FileOpsCore>,
357 current_task: &CurrentTask,
358 offset: usize,
359 data: &mut dyn InputBuffer,
360 ) -> Result<usize, Errno> {
361 self.source.write(locked, current_task, offset, data)
362 }
363}
364
365#[derive(Debug, Default)]
366pub struct DynamicFileBuf(VecDeque<u8>);
367impl DynamicFileBuf {
368 pub fn write(&mut self, data: &[u8]) {
369 self.0.extend(data.iter().copied());
370 }
371 pub fn write_iter<I>(&mut self, data: I)
372 where
373 I: IntoIterator<Item = u8>,
374 {
375 self.0.extend(data);
376 }
377 pub fn write_fmt(&mut self, args: std::fmt::Arguments<'_>) -> Result<usize, Errno> {
378 let start_size = self.0.len();
379 std::io::Write::write_fmt(&mut self.0, args).map_err(|_| errno!(EINVAL))?;
380 let end_size = self.0.len();
381 Ok(end_size - start_size)
382 }
383}
384
385impl Buffer for DynamicFileBuf {
386 fn segments_count(&self) -> Result<usize, Errno> {
387 std::unimplemented!();
388 }
389
390 fn peek_each_segment(
391 &mut self,
392 _callback: &mut PeekBufferSegmentsCallback<'_>,
393 ) -> Result<(), Errno> {
394 std::unimplemented!();
395 }
396}
397
398impl OutputBuffer for DynamicFileBuf {
399 fn available(&self) -> usize {
400 std::unimplemented!();
401 }
402
403 fn bytes_written(&self) -> usize {
404 std::unimplemented!();
405 }
406
407 fn zero(&mut self) -> Result<usize, Errno> {
408 std::unimplemented!();
409 }
410
411 fn write_each(&mut self, _callback: &mut OutputBufferCallback<'_>) -> Result<usize, Errno> {
412 std::unimplemented!();
413 }
414
415 fn write_all(&mut self, buffer: &[u8]) -> Result<usize, Errno> {
416 self.write(buffer);
417 Ok(buffer.len())
418 }
419
420 unsafe fn advance(&mut self, _length: usize) -> Result<(), Errno> {
421 std::unimplemented!();
422 }
423}
424
425pub struct ConstFile {
427 data: Vec<u8>,
428}
429
430impl DynamicFileSource for ConstFile {
431 fn generate(
432 &self,
433 _current_task: &CurrentTask,
434 sink: &mut DynamicFileBuf,
435 ) -> Result<(), Errno> {
436 sink.write(&self.data);
437 Ok(())
438 }
439
440 fn write(
441 &self,
442 _locked: &mut Locked<FileOpsCore>,
443 _current_task: &CurrentTask,
444 _offset: usize,
445 data: &mut dyn InputBuffer,
446 ) -> Result<usize, Errno> {
447 Ok(data.drain())
448 }
449}
450
451impl ConstFile {
452 pub fn new_node(data: Vec<u8>) -> impl FsNodeOps {
454 SimpleFileNode::new(move |_, _| Ok(DynamicFile::new(ConstFile { data: data.clone() })))
455 }
456}
457
458#[cfg(test)]
459mod tests {
460 use crate::task::CurrentTask;
461 use crate::testing::{anon_test_file, spawn_kernel_and_run};
462 use crate::vfs::pseudo::dynamic_file::{
463 DynamicFile, DynamicFileBuf, DynamicFileSource, SequenceFileSource,
464 };
465 use crate::vfs::{SeekTarget, VecOutputBuffer};
466 use starnix_sync::{Locked, Mutex, Unlocked};
467 use starnix_uapi::errors::Errno;
468 use starnix_uapi::open_flags::OpenFlags;
469 use std::sync::Arc;
470
471 struct Counter {
472 value: Mutex<u8>,
473 }
474
475 struct TestSequenceFileSource;
476
477 impl SequenceFileSource for TestSequenceFileSource {
478 type Cursor = u8;
479 fn next(
480 &self,
481 _current_task: &CurrentTask,
482 i: u8,
483 sink: &mut DynamicFileBuf,
484 ) -> Result<Option<u8>, Errno> {
485 sink.write(&[i]);
486 Ok(if i == u8::MAX { None } else { Some(i + 1) })
487 }
488 }
489
490 #[fuchsia::test]
491 async fn test_sequence() {
492 spawn_kernel_and_run(async |locked, current_task| {
493 let file = anon_test_file(
494 locked,
495 ¤t_task,
496 Box::new(DynamicFile::new(TestSequenceFileSource {})),
497 OpenFlags::RDONLY,
498 );
499
500 let read_at = |locked: &mut Locked<Unlocked>,
501 offset: usize,
502 length: usize|
503 -> Result<Vec<u8>, Errno> {
504 let mut buffer = VecOutputBuffer::new(length);
505 file.read_at(locked, ¤t_task, offset, &mut buffer)?;
506 Ok(buffer.data().to_vec())
507 };
508
509 assert_eq!(read_at(locked, 0, 2).unwrap(), &[0, 1]);
510 assert_eq!(read_at(locked, 2, 2).unwrap(), &[2, 3]);
511 assert_eq!(read_at(locked, 4, 4).unwrap(), &[4, 5, 6, 7]);
512 assert_eq!(read_at(locked, 0, 2).unwrap(), &[0, 1]);
513 assert_eq!(read_at(locked, 4, 2).unwrap(), &[4, 5]);
514 })
515 .await;
516 }
517
518 struct TestFileSource {
519 counter: Arc<Counter>,
520 }
521
522 impl DynamicFileSource for TestFileSource {
523 fn generate(
524 &self,
525 _current_task: &CurrentTask,
526 sink: &mut DynamicFileBuf,
527 ) -> Result<(), Errno> {
528 let mut counter = self.counter.value.lock();
529 let base = *counter;
530 let data = (0..10).map(|i| base + i).collect::<Vec<u8>>();
532 sink.write(&data);
533 *counter += 1;
534 Ok(())
535 }
536 }
537
538 #[fuchsia::test]
539 async fn test_read() {
540 let counter = Arc::new(Counter { value: Mutex::new(0) });
541 spawn_kernel_and_run(async move |locked, current_task| {
542 let file = anon_test_file(
543 locked,
544 ¤t_task,
545 Box::new(DynamicFile::new(TestFileSource { counter: counter.clone() })),
546 OpenFlags::RDONLY,
547 );
548 let read_at = |locked: &mut Locked<Unlocked>,
549 offset: usize,
550 length: usize|
551 -> Result<Vec<u8>, Errno> {
552 let mut buffer = VecOutputBuffer::new(length);
553 let bytes_read = file.read_at(locked, ¤t_task, offset, &mut buffer)?;
554 Ok(buffer.data()[0..bytes_read].to_vec())
555 };
556
557 assert_eq!(read_at(locked, 0, 20).unwrap(), (0..10).collect::<Vec<u8>>());
559
560 assert_eq!(read_at(locked, 0, 2).unwrap(), [1, 2]);
562
563 assert_eq!(read_at(locked, 2, 2).unwrap(), [3, 4]);
565
566 assert_eq!(read_at(locked, 5, 2).unwrap(), [7, 8]);
568 })
569 .await;
570 }
571
572 #[fuchsia::test]
573 async fn test_read_and_seek() {
574 let counter = Arc::new(Counter { value: Mutex::new(0) });
575 spawn_kernel_and_run(async move |locked, current_task| {
576 let file = anon_test_file(
577 locked,
578 ¤t_task,
579 Box::new(DynamicFile::new(TestFileSource { counter: counter.clone() })),
580 OpenFlags::RDONLY,
581 );
582 let read = |locked: &mut Locked<Unlocked>, length: usize| -> Result<Vec<u8>, Errno> {
583 let mut buffer = VecOutputBuffer::new(length);
584 let bytes_read = file.read(locked, ¤t_task, &mut buffer)?;
585 Ok(buffer.data()[0..bytes_read].to_vec())
586 };
587
588 assert_eq!(read(locked, 1).unwrap(), [0]);
590 assert_eq!(read(locked, 2).unwrap(), [1, 2]);
591 assert_eq!(read(locked, 20).unwrap(), (3..10).collect::<Vec<u8>>());
592
593 file.seek(locked, ¤t_task, SeekTarget::Set(0)).unwrap();
595 assert_eq!(*counter.value.lock(), 1);
596 assert_eq!(read(locked, 2).unwrap(), [1, 2]);
597 assert_eq!(*counter.value.lock(), 2);
598
599 file.seek(locked, ¤t_task, SeekTarget::Set(1)).unwrap();
601 assert_eq!(*counter.value.lock(), 2);
602
603 assert_eq!(read(locked, 1).unwrap(), [3]);
605 assert_eq!(*counter.value.lock(), 3);
606 })
607 .await;
608 }
609}