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