1use crate::task::CurrentTask;
6use crate::vfs::buffers::{InputBuffer, OutputBuffer, VecOutputBuffer};
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 let new_offset = default_seek(current_offset, target, || error!(EINVAL))?;
262
263 if new_offset > 0 {
266 let mut dummy_buf = VecOutputBuffer::new(0);
267 self.read_internal(locked, current_task, new_offset as usize, &mut dummy_buf)?;
268 }
269
270 Ok(new_offset)
271 }
272}
273
274struct DynamicFileState<Source: SequenceFileSource> {
276 source: Source,
278
279 cursor: Option<Source::Cursor>,
282
283 buf: DynamicFileBuf,
286
287 byte_offset: usize,
294}
295
296impl<Source: SequenceFileSource> DynamicFileState<Source> {
297 fn new(source: Source) -> Self {
298 Self {
299 source,
300 cursor: Some(Source::Cursor::default()),
301 buf: DynamicFileBuf::default(),
302 byte_offset: 0,
303 }
304 }
305}
306
307impl<Source: SequenceFileSource> DynamicFileState<Source> {
308 fn reset(&mut self) {
309 self.cursor = Some(Source::Cursor::default());
310 self.buf = DynamicFileBuf::default();
311 self.byte_offset = 0;
312 }
313
314 fn read(
315 &mut self,
316 locked: &mut Locked<FileOpsCore>,
317 current_task: &CurrentTask,
318 offset: usize,
319 data: &mut dyn OutputBuffer,
320 ) -> Result<usize, Errno> {
321 if offset != self.byte_offset {
322 self.reset();
323 }
324 let read_size = data.available();
325
326 while self.byte_offset + self.buf.0.len() < offset + read_size {
328 let cursor = if let Some(cursor) = std::mem::take(&mut self.cursor) {
329 cursor
330 } else {
331 break;
332 };
333 let mut buf = std::mem::take(&mut self.buf);
334 self.cursor =
335 self.source.next_locked(locked, current_task, cursor, &mut buf).map_err(|e| {
336 self.reset();
338 e
339 })?;
340 self.buf = buf;
341
342 let to_drain = std::cmp::min(offset - self.byte_offset, self.buf.0.len());
345 self.buf.0.drain(..to_drain);
346 self.byte_offset += to_drain;
347 }
348
349 let (slice1, slice2) = self.buf.0.as_slices();
352 let mut written = data.write(slice1)?;
353 if written == slice1.len() && !slice2.is_empty() {
354 written += data.write(slice2)?;
355 }
356
357 self.buf.0.drain(..written);
359 self.byte_offset += written;
360 Ok(written)
361 }
362
363 fn write(
364 &mut self,
365 locked: &mut Locked<FileOpsCore>,
366 current_task: &CurrentTask,
367 offset: usize,
368 data: &mut dyn InputBuffer,
369 ) -> Result<usize, Errno> {
370 self.source.write(locked, current_task, offset, data)
371 }
372}
373
374#[derive(Debug, Default)]
375pub struct DynamicFileBuf(VecDeque<u8>);
376impl DynamicFileBuf {
377 pub fn write(&mut self, data: &[u8]) {
378 self.0.extend(data.iter().copied());
379 }
380 pub fn write_iter<I>(&mut self, data: I)
381 where
382 I: IntoIterator<Item = u8>,
383 {
384 self.0.extend(data);
385 }
386 pub fn write_fmt(&mut self, args: std::fmt::Arguments<'_>) -> Result<usize, Errno> {
387 let start_size = self.0.len();
388 std::io::Write::write_fmt(&mut self.0, args).map_err(|_| errno!(EINVAL))?;
389 let end_size = self.0.len();
390 Ok(end_size - start_size)
391 }
392}
393
394impl Buffer for DynamicFileBuf {
395 fn segments_count(&self) -> Result<usize, Errno> {
396 std::unimplemented!();
397 }
398
399 fn peek_each_segment(
400 &mut self,
401 _callback: &mut PeekBufferSegmentsCallback<'_>,
402 ) -> Result<(), Errno> {
403 std::unimplemented!();
404 }
405}
406
407impl OutputBuffer for DynamicFileBuf {
408 fn available(&self) -> usize {
409 std::unimplemented!();
410 }
411
412 fn bytes_written(&self) -> usize {
413 std::unimplemented!();
414 }
415
416 fn zero(&mut self) -> Result<usize, Errno> {
417 std::unimplemented!();
418 }
419
420 fn write_each(&mut self, _callback: &mut OutputBufferCallback<'_>) -> Result<usize, Errno> {
421 std::unimplemented!();
422 }
423
424 fn write_all(&mut self, buffer: &[u8]) -> Result<usize, Errno> {
425 self.write(buffer);
426 Ok(buffer.len())
427 }
428
429 unsafe fn advance(&mut self, _length: usize) -> Result<(), Errno> {
430 std::unimplemented!();
431 }
432}
433
434pub struct ConstFile {
436 data: Vec<u8>,
437}
438
439impl DynamicFileSource for ConstFile {
440 fn generate(
441 &self,
442 _current_task: &CurrentTask,
443 sink: &mut DynamicFileBuf,
444 ) -> Result<(), Errno> {
445 sink.write(&self.data);
446 Ok(())
447 }
448
449 fn write(
450 &self,
451 _locked: &mut Locked<FileOpsCore>,
452 _current_task: &CurrentTask,
453 _offset: usize,
454 data: &mut dyn InputBuffer,
455 ) -> Result<usize, Errno> {
456 Ok(data.drain())
457 }
458}
459
460impl ConstFile {
461 pub fn new_node(data: Vec<u8>) -> impl FsNodeOps {
463 SimpleFileNode::new(move |_, _| Ok(DynamicFile::new(ConstFile { data: data.clone() })))
464 }
465}
466
467#[cfg(test)]
468mod tests {
469 use crate::task::CurrentTask;
470 use crate::testing::{anon_test_file, spawn_kernel_and_run};
471 use crate::vfs::pseudo::dynamic_file::{
472 DynamicFile, DynamicFileBuf, DynamicFileSource, SequenceFileSource,
473 };
474 use crate::vfs::{SeekTarget, VecOutputBuffer};
475 use starnix_sync::{Locked, Mutex, Unlocked};
476 use starnix_uapi::errors::Errno;
477 use starnix_uapi::open_flags::OpenFlags;
478 use std::sync::Arc;
479
480 struct Counter {
481 value: Mutex<u8>,
482 }
483
484 struct TestSequenceFileSource;
485
486 impl SequenceFileSource for TestSequenceFileSource {
487 type Cursor = u8;
488 fn next(
489 &self,
490 _current_task: &CurrentTask,
491 i: u8,
492 sink: &mut DynamicFileBuf,
493 ) -> Result<Option<u8>, Errno> {
494 sink.write(&[i]);
495 Ok(if i == u8::MAX { None } else { Some(i + 1) })
496 }
497 }
498
499 #[fuchsia::test]
500 async fn test_sequence() {
501 spawn_kernel_and_run(async |locked, current_task| {
502 let file = anon_test_file(
503 locked,
504 ¤t_task,
505 Box::new(DynamicFile::new(TestSequenceFileSource {})),
506 OpenFlags::RDONLY,
507 );
508
509 let read_at = |locked: &mut Locked<Unlocked>,
510 offset: usize,
511 length: usize|
512 -> Result<Vec<u8>, Errno> {
513 let mut buffer = VecOutputBuffer::new(length);
514 file.read_at(locked, ¤t_task, offset, &mut buffer)?;
515 Ok(buffer.data().to_vec())
516 };
517
518 assert_eq!(read_at(locked, 0, 2).unwrap(), &[0, 1]);
519 assert_eq!(read_at(locked, 2, 2).unwrap(), &[2, 3]);
520 assert_eq!(read_at(locked, 4, 4).unwrap(), &[4, 5, 6, 7]);
521 assert_eq!(read_at(locked, 0, 2).unwrap(), &[0, 1]);
522 assert_eq!(read_at(locked, 4, 2).unwrap(), &[4, 5]);
523 })
524 .await;
525 }
526
527 struct TestFileSource {
528 counter: Arc<Counter>,
529 }
530
531 impl DynamicFileSource for TestFileSource {
532 fn generate(
533 &self,
534 _current_task: &CurrentTask,
535 sink: &mut DynamicFileBuf,
536 ) -> Result<(), Errno> {
537 let mut counter = self.counter.value.lock();
538 let base = *counter;
539 let data = (0..10).map(|i| base + i).collect::<Vec<u8>>();
541 sink.write(&data);
542 *counter += 1;
543 Ok(())
544 }
545 }
546
547 #[fuchsia::test]
548 async fn test_read() {
549 let counter = Arc::new(Counter { value: Mutex::new(0) });
550 spawn_kernel_and_run(async move |locked, current_task| {
551 let file = anon_test_file(
552 locked,
553 ¤t_task,
554 Box::new(DynamicFile::new(TestFileSource { counter: counter.clone() })),
555 OpenFlags::RDONLY,
556 );
557 let read_at = |locked: &mut Locked<Unlocked>,
558 offset: usize,
559 length: usize|
560 -> Result<Vec<u8>, Errno> {
561 let mut buffer = VecOutputBuffer::new(length);
562 let bytes_read = file.read_at(locked, ¤t_task, offset, &mut buffer)?;
563 Ok(buffer.data()[0..bytes_read].to_vec())
564 };
565
566 assert_eq!(read_at(locked, 0, 20).unwrap(), (0..10).collect::<Vec<u8>>());
568
569 assert_eq!(read_at(locked, 0, 2).unwrap(), [1, 2]);
571
572 assert_eq!(read_at(locked, 2, 2).unwrap(), [3, 4]);
574
575 assert_eq!(read_at(locked, 5, 2).unwrap(), [7, 8]);
577 })
578 .await;
579 }
580
581 #[fuchsia::test]
582 async fn test_read_and_seek() {
583 let counter = Arc::new(Counter { value: Mutex::new(0) });
584 spawn_kernel_and_run(async move |locked, current_task| {
585 let file = anon_test_file(
586 locked,
587 ¤t_task,
588 Box::new(DynamicFile::new(TestFileSource { counter: counter.clone() })),
589 OpenFlags::RDONLY,
590 );
591 let read = |locked: &mut Locked<Unlocked>, length: usize| -> Result<Vec<u8>, Errno> {
592 let mut buffer = VecOutputBuffer::new(length);
593 let bytes_read = file.read(locked, ¤t_task, &mut buffer)?;
594 Ok(buffer.data()[0..bytes_read].to_vec())
595 };
596
597 assert_eq!(read(locked, 1).unwrap(), [0]);
599 assert_eq!(read(locked, 2).unwrap(), [1, 2]);
600 assert_eq!(read(locked, 20).unwrap(), (3..10).collect::<Vec<u8>>());
601
602 file.seek(locked, ¤t_task, SeekTarget::Set(0)).unwrap();
604 assert_eq!(*counter.value.lock(), 1);
605 assert_eq!(read(locked, 2).unwrap(), [1, 2]);
606 assert_eq!(*counter.value.lock(), 2);
607
608 file.seek(locked, ¤t_task, SeekTarget::Set(1)).unwrap();
610 assert_eq!(*counter.value.lock(), 3);
611 })
612 .await;
613 }
614}