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::{DynamicFileStateLock, LockDepMutex};
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 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 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
108pub 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
253struct DynamicFileState<Source: SequenceFileSource> {
255 source: Source,
257
258 cursor: Option<Source::Cursor>,
261
262 buf: DynamicFileBuf,
265
266 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 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 self.reset();
315 e
316 })?;
317 self.buf = buf;
318
319 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 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 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.iter().copied());
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
370impl Buffer for DynamicFileBuf {
371 fn segments_count(&self) -> Result<usize, Errno> {
372 std::unimplemented!();
373 }
374
375 fn peek_each_segment(
376 &mut self,
377 _callback: &mut PeekBufferSegmentsCallback<'_>,
378 ) -> Result<(), Errno> {
379 std::unimplemented!();
380 }
381}
382
383impl OutputBuffer for DynamicFileBuf {
384 fn available(&self) -> usize {
385 std::unimplemented!();
386 }
387
388 fn bytes_written(&self) -> usize {
389 std::unimplemented!();
390 }
391
392 fn zero(&mut self) -> Result<usize, Errno> {
393 std::unimplemented!();
394 }
395
396 fn write_each(&mut self, _callback: &mut OutputBufferCallback<'_>) -> Result<usize, Errno> {
397 std::unimplemented!();
398 }
399
400 fn write_all(&mut self, buffer: &[u8]) -> Result<usize, Errno> {
401 self.write(buffer);
402 Ok(buffer.len())
403 }
404
405 unsafe fn advance(&mut self, _length: usize) -> Result<(), Errno> {
406 std::unimplemented!();
407 }
408}
409
410pub struct ConstFile {
412 data: Vec<u8>,
413}
414
415impl DynamicFileSource for ConstFile {
416 fn generate(
417 &self,
418 _current_task: &CurrentTask,
419 sink: &mut DynamicFileBuf,
420 ) -> Result<(), Errno> {
421 sink.write(&self.data);
422 Ok(())
423 }
424
425 fn write(
426 &self,
427 _current_task: &CurrentTask,
428 _offset: usize,
429 data: &mut dyn InputBuffer,
430 ) -> Result<usize, Errno> {
431 Ok(data.drain())
432 }
433}
434
435impl ConstFile {
436 pub fn new_node(data: Vec<u8>) -> impl FsNodeOps {
438 SimpleFileNode::new(move |_| Ok(DynamicFile::new(ConstFile { data: data.clone() })))
439 }
440}
441
442#[cfg(test)]
443mod tests {
444 use crate::task::CurrentTask;
445 use crate::testing::{anon_test_file, spawn_kernel_and_run};
446 use crate::vfs::pseudo::dynamic_file::{
447 DynamicFile, DynamicFileBuf, DynamicFileSource, SequenceFileSource,
448 };
449 use crate::vfs::{SeekTarget, VecOutputBuffer};
450 use starnix_sync::Mutex;
451 use starnix_uapi::errors::Errno;
452 use starnix_uapi::open_flags::OpenFlags;
453 use std::sync::Arc;
454
455 struct Counter {
456 value: Mutex<u8>,
457 }
458
459 struct TestSequenceFileSource;
460
461 impl SequenceFileSource for TestSequenceFileSource {
462 type Cursor = u8;
463 fn next(
464 &self,
465 _current_task: &CurrentTask,
466 i: u8,
467 sink: &mut DynamicFileBuf,
468 ) -> Result<Option<u8>, Errno> {
469 sink.write(&[i]);
470 Ok(if i == u8::MAX { None } else { Some(i + 1) })
471 }
472 }
473
474 #[fuchsia::test]
475 async fn test_sequence() {
476 spawn_kernel_and_run(async |current_task| {
477 let file = anon_test_file(
478 ¤t_task,
479 Box::new(DynamicFile::new(TestSequenceFileSource {})),
480 OpenFlags::RDONLY,
481 );
482
483 let read_at = |offset: usize, length: usize| -> Result<Vec<u8>, Errno> {
484 let mut buffer = VecOutputBuffer::new(length);
485 file.read_at(¤t_task, offset, &mut buffer)?;
486 Ok(buffer.data().to_vec())
487 };
488
489 assert_eq!(read_at(0, 2).unwrap(), &[0, 1]);
490 assert_eq!(read_at(2, 2).unwrap(), &[2, 3]);
491 assert_eq!(read_at(4, 4).unwrap(), &[4, 5, 6, 7]);
492 assert_eq!(read_at(0, 2).unwrap(), &[0, 1]);
493 assert_eq!(read_at(4, 2).unwrap(), &[4, 5]);
494 })
495 .await;
496 }
497
498 struct TestFileSource {
499 counter: Arc<Counter>,
500 }
501
502 impl DynamicFileSource for TestFileSource {
503 fn generate(
504 &self,
505 _current_task: &CurrentTask,
506 sink: &mut DynamicFileBuf,
507 ) -> Result<(), Errno> {
508 let mut counter = self.counter.value.lock();
509 let base = *counter;
510 let data = (0..10).map(|i| base + i).collect::<Vec<u8>>();
512 sink.write(&data);
513 *counter += 1;
514 Ok(())
515 }
516 }
517
518 #[fuchsia::test]
519 async fn test_read() {
520 let counter = Arc::new(Counter { value: Mutex::new(0) });
521 spawn_kernel_and_run(async move |current_task| {
522 let file = anon_test_file(
523 ¤t_task,
524 Box::new(DynamicFile::new(TestFileSource { counter: counter.clone() })),
525 OpenFlags::RDONLY,
526 );
527 let read_at = |offset: usize, length: usize| -> Result<Vec<u8>, Errno> {
528 let mut buffer = VecOutputBuffer::new(length);
529 let bytes_read = file.read_at(¤t_task, offset, &mut buffer)?;
530 Ok(buffer.data()[0..bytes_read].to_vec())
531 };
532
533 assert_eq!(read_at(0, 20).unwrap(), (0..10).collect::<Vec<u8>>());
535
536 assert_eq!(read_at(0, 2).unwrap(), [1, 2]);
538
539 assert_eq!(read_at(2, 2).unwrap(), [3, 4]);
541
542 assert_eq!(read_at(5, 2).unwrap(), [7, 8]);
544 })
545 .await;
546 }
547
548 #[fuchsia::test]
549 async fn test_read_and_seek() {
550 let counter = Arc::new(Counter { value: Mutex::new(0) });
551 spawn_kernel_and_run(async move |current_task| {
552 let file = anon_test_file(
553 ¤t_task,
554 Box::new(DynamicFile::new(TestFileSource { counter: counter.clone() })),
555 OpenFlags::RDONLY,
556 );
557 let read = |length: usize| -> Result<Vec<u8>, Errno> {
558 let mut buffer = VecOutputBuffer::new(length);
559 let bytes_read = file.read(¤t_task, &mut buffer)?;
560 Ok(buffer.data()[0..bytes_read].to_vec())
561 };
562
563 assert_eq!(read(1).unwrap(), [0]);
565 assert_eq!(read(2).unwrap(), [1, 2]);
566 assert_eq!(read(20).unwrap(), (3..10).collect::<Vec<u8>>());
567
568 file.seek(¤t_task, SeekTarget::Set(0)).unwrap();
570 assert_eq!(*counter.value.lock(), 1);
571 assert_eq!(read(2).unwrap(), [1, 2]);
572 assert_eq!(*counter.value.lock(), 2);
573
574 file.seek(¤t_task, SeekTarget::Set(1)).unwrap();
576 assert_eq!(*counter.value.lock(), 2);
577
578 assert_eq!(read(1).unwrap(), [3]);
580 assert_eq!(*counter.value.lock(), 3);
581 })
582 .await;
583 }
584}