1#![recursion_limit = "256"]
6
7use fidl::endpoints::SynchronousProxy;
8use fidl_fuchsia_hardware_adb as fadb;
9use fuchsia_async as fasync;
10use futures_util::StreamExt;
11use starnix_core::power::{create_proxy_for_wake_events_counter_zero, mark_proxy_message_handled};
12use starnix_core::task::{CurrentTask, EventHandler, Kernel, WaitCanceler, WaitQueue, Waiter};
13use starnix_core::vfs::pseudo::vec_directory::{VecDirectory, VecDirectoryEntry};
14use starnix_core::vfs::{
15 CacheMode, DirectoryEntryType, FileObject, FileObjectState, FileOps, FileSystem,
16 FileSystemHandle, FileSystemOps, FileSystemOptions, FsNode, FsNodeInfo, FsNodeOps, FsStr,
17 InputBuffer, OutputBuffer, fileops_impl_noop_sync, fileops_impl_seekless, fs_args,
18 fs_node_impl_dir_readonly, fs_node_impl_not_dir,
19};
20use starnix_logging::{log_error, log_warn, track_stub};
21use starnix_sync::{FileOpsCore, Locked, Mutex, Unlocked};
22use starnix_types::vfs::default_statfs;
23use starnix_uapi::auth::FsCred;
24use starnix_uapi::errors::Errno;
25use starnix_uapi::file_mode::mode;
26use starnix_uapi::open_flags::OpenFlags;
27use starnix_uapi::vfs::FdEvents;
28use starnix_uapi::{
29 errno, error, gid_t, ino_t, statfs, uid_t, usb_functionfs_event,
30 usb_functionfs_event_type_FUNCTIONFS_BIND, usb_functionfs_event_type_FUNCTIONFS_DISABLE,
31 usb_functionfs_event_type_FUNCTIONFS_ENABLE, usb_functionfs_event_type_FUNCTIONFS_UNBIND,
32};
33use std::collections::VecDeque;
34use std::ops::Deref;
35use std::sync::{Arc, mpsc};
36use zerocopy::IntoBytes;
37
38const ROOT_NODE_ID: ino_t = 1;
40
41const CONTROL_ENDPOINT: &str = "ep0";
43const CONTROL_ENDPOINT_NODE_ID: ino_t = 2;
44
45const OUTPUT_ENDPOINT: &str = "ep1";
46const OUTPUT_ENDPOINT_NODE_ID: ino_t = 3;
47
48const INPUT_ENDPOINT: &str = "ep2";
49const INPUT_ENDPOINT_NODE_ID: ino_t = 4;
50
51const FUNCTIONFS_MAGIC: u32 = 0xa647361;
54
55const ADB_DIRECTORY: &str = "/svc/fuchsia.hardware.adb.Service";
56
57const ADB_INTERACTION_TIMEOUT: zx::Duration<zx::MonotonicTimeline> = zx::Duration::from_seconds(2);
60
61struct ReadCommand {
62 response_sender: mpsc::Sender<Result<Vec<u8>, Errno>>,
63}
64
65struct WriteCommand {
66 data: Vec<u8>,
67 response_sender: mpsc::Sender<Result<usize, Errno>>,
68}
69
70async fn handle_adb(
79 proxy: fadb::UsbAdbImpl_Proxy,
80 message_counter: Option<zx::Counter>,
81 read_commands: async_channel::Receiver<ReadCommand>,
82 write_commands: async_channel::Receiver<WriteCommand>,
83 state: Arc<Mutex<FunctionFsState>>,
84) {
85 async fn handle_events(
101 mut stream: fadb::UsbAdbImpl_EventStream,
102 message_counter: &Option<zx::Counter>,
103 state: Arc<Mutex<FunctionFsState>>,
104 ) {
105 let queue_event = |event| {
106 let mut state_locked = state.lock();
107 state_locked
108 .event_queue
109 .push_back(usb_functionfs_event { type_: event as u8, ..Default::default() });
110 state_locked.waiters.notify_fd_events(FdEvents::POLLIN);
111 };
112
113 queue_event(usb_functionfs_event_type_FUNCTIONFS_BIND);
114
115 while let Some(Ok(fadb::UsbAdbImpl_Event::OnStatusChanged { status })) = stream.next().await
116 {
117 let is_online = status == fadb::StatusFlags::ONLINE;
118 {
119 let mut state_locked = state.lock();
120 state_locked.is_online = is_online;
121 state_locked.event_queue.push_back(usb_functionfs_event {
122 type_: if is_online {
123 usb_functionfs_event_type_FUNCTIONFS_ENABLE
124 } else {
125 usb_functionfs_event_type_FUNCTIONFS_DISABLE
126 } as u8,
127 ..Default::default()
128 });
129 state_locked.waiters.notify_fd_events(FdEvents::POLLIN);
130 state_locked.waiters.notify_all();
131 }
132
133 message_counter.as_ref().map(mark_proxy_message_handled);
137 }
138
139 queue_event(usb_functionfs_event_type_FUNCTIONFS_UNBIND);
140 }
141
142 async fn handle_idle_timeouts(
151 timeouts: async_channel::Receiver<zx::MonotonicInstant>,
152 message_counter: &Option<zx::Counter>,
153 ) {
154 timeouts
155 .for_each(|timeout| async move {
156 use fasync::WakeupTime;
157 timeout.into_timer().await;
158 message_counter.as_ref().map(mark_proxy_message_handled);
159 })
160 .await
161 }
162
163 async fn handle_read_commands(
165 proxy: &fadb::UsbAdbImpl_Proxy,
166 timeouts_sender: async_channel::Sender<zx::MonotonicInstant>,
167 commands: async_channel::Receiver<ReadCommand>,
168 ) {
169 let timeouts_sender = &timeouts_sender;
170 commands
171 .for_each(|ReadCommand { response_sender }| async move {
172 let receive_future = proxy.receive();
175
176 timeouts_sender
181 .send(zx::MonotonicInstant::after(ADB_INTERACTION_TIMEOUT))
182 .await
183 .expect("Should be able to send timeout");
184
185 let response = match receive_future.await {
186 Err(err) => {
187 log_warn!("Failed to call UsbAdbImpl.Receive: {err}");
188 error!(EINVAL)
189 }
190 Ok(Err(err)) => {
191 log_warn!("Failed to receive data from adb driver: {err}");
192 error!(EINVAL)
193 }
194 Ok(Ok(payload)) => Ok(payload),
195 };
196
197 response_sender
198 .send(response)
199 .map_err(|e| log_error!("Failed to send to main thread: {:#?}", e))
200 .ok();
201 })
202 .await;
203 }
204
205 async fn handle_write_commands(
207 proxy: &fadb::UsbAdbImpl_Proxy,
208 timeouts_sender: async_channel::Sender<zx::MonotonicInstant>,
209 commands: async_channel::Receiver<WriteCommand>,
210 ) {
211 let timeouts_sender = &timeouts_sender;
212 commands
213 .for_each(|WriteCommand { data, response_sender }| async move {
214 let response = match proxy.queue_tx(&data).await {
215 Err(err) => {
216 log_warn!("Failed to call UsbAdbImpl.QueueTx: {err}");
217 error!(EINVAL)
218 }
219 Ok(Err(err)) => {
220 log_warn!("Failed to queue data to adb driver: {err}");
221 error!(EINVAL)
222 }
223 Ok(Ok(_)) => Ok(data.len()),
224 };
225
226 timeouts_sender
230 .send(zx::MonotonicInstant::after(ADB_INTERACTION_TIMEOUT))
231 .await
232 .expect("Should be able to send timeout");
233
234 response_sender
235 .send(response)
236 .map_err(|e| log_error!("Failed to send to main thread: {:#?}", e))
237 .ok();
238 })
239 .await;
240 }
241
242 let (timeouts_sender, timeouts_receiver) = async_channel::unbounded();
243 let event_future = handle_events(proxy.take_event_stream(), &message_counter, state);
244 let write_commands_future =
245 handle_write_commands(&proxy, timeouts_sender.clone(), write_commands);
246 let read_commands_future = handle_read_commands(&proxy, timeouts_sender, read_commands);
247 let timeout_future = handle_idle_timeouts(timeouts_receiver, &message_counter);
248 futures::join!(event_future, write_commands_future, read_commands_future, timeout_future);
249}
250
251pub struct FunctionFs;
252impl FunctionFs {
253 pub fn new_fs(
254 locked: &mut Locked<Unlocked>,
255 current_task: &CurrentTask,
256 options: FileSystemOptions,
257 ) -> Result<FileSystemHandle, Errno> {
258 if options.source != "adb" {
259 track_stub!(TODO("https://fxbug.dev/329699340"), "FunctionFS supports other uses");
260 return error!(ENODEV);
261 }
262
263 if let Err(e) = std::fs::read_dir(ADB_DIRECTORY) {
267 log_warn!(
268 "Attempted to mount FunctionFS for adb, but could not read {ADB_DIRECTORY}: {e}"
269 );
270 return error!(ENODEV);
271 }
272
273 let uid = if let Some(uid) = options.params.get(b"uid") {
274 fs_args::parse::<uid_t>(uid.as_ref())?
275 } else {
276 0
277 };
278 let gid = if let Some(gid) = options.params.get(b"gid") {
279 fs_args::parse::<gid_t>(gid.as_ref())?
280 } else {
281 0
282 };
283
284 let fs = FileSystem::new(
285 locked,
286 current_task.kernel(),
287 CacheMode::Uncached,
288 FunctionFs,
289 options,
290 )?;
291
292 let creds = FsCred { uid, gid };
293 let info = FsNodeInfo::new(mode!(IFDIR, 0o777), creds);
294 fs.create_root_with_info(ROOT_NODE_ID, FunctionFsRootDir::default(), info);
295 Ok(fs)
296 }
297}
298
299impl FileSystemOps for FunctionFs {
300 fn statfs(
301 &self,
302 _locked: &mut Locked<FileOpsCore>,
303 _fs: &FileSystem,
304 _current_task: &CurrentTask,
305 ) -> Result<statfs, Errno> {
306 Ok(default_statfs(FUNCTIONFS_MAGIC))
307 }
308
309 fn name(&self) -> &'static FsStr {
310 b"functionfs".into()
311 }
312}
313
314#[derive(Default)]
315struct FunctionFsState {
316 num_control_file_objects: usize,
320
321 has_input_output_endpoints: bool,
324
325 is_online: bool,
327
328 adb_read_channel: Option<async_channel::Sender<ReadCommand>>,
329 adb_write_channel: Option<async_channel::Sender<WriteCommand>>,
330
331 device_proxy: Option<fadb::DeviceSynchronousProxy>,
333
334 event_queue: VecDeque<usb_functionfs_event>,
337
338 waiters: WaitQueue,
339}
340
341pub enum AdbProxyMode {
342 None,
344
345 WakeContainer,
349}
350
351fn connect_to_device(
352 proxy: AdbProxyMode,
353) -> Result<
354 (fadb::DeviceSynchronousProxy, fadb::UsbAdbImpl_SynchronousProxy, Option<zx::Counter>),
355 Errno,
356> {
357 let mut dir = std::fs::read_dir(ADB_DIRECTORY).map_err(|_| errno!(EINVAL))?;
358
359 let Some(Ok(entry)) = dir.next() else {
360 return error!(EBUSY);
361 };
362 let path =
363 entry.path().join("adb").into_os_string().into_string().map_err(|_| errno!(EINVAL))?;
364
365 let (client_channel, server_channel) = zx::Channel::create();
366 fdio::service_connect(&path, server_channel).map_err(|_| errno!(EINVAL))?;
367 let device_proxy = fadb::DeviceSynchronousProxy::new(client_channel);
368
369 let (adb_proxy, server_end) = fidl::endpoints::create_sync_proxy::<fadb::UsbAdbImpl_Marker>();
370 let (adb_proxy, message_counter) = match proxy {
371 AdbProxyMode::None => (adb_proxy, None),
372 AdbProxyMode::WakeContainer => {
373 let (adb_proxy, message_counter) = create_proxy_for_wake_events_counter_zero(
374 adb_proxy.into_channel(),
375 "adb".to_string(),
376 );
377 let adb_proxy = fadb::UsbAdbImpl_SynchronousProxy::from_channel(adb_proxy);
378 (adb_proxy, Some(message_counter))
379 }
380 };
381
382 device_proxy
383 .start_adb(server_end, zx::MonotonicInstant::INFINITE)
384 .map_err(|_| errno!(EINVAL))?
385 .map_err(|_| errno!(EINVAL))?;
386 return Ok((device_proxy, adb_proxy, message_counter));
387}
388
389#[derive(Default)]
390struct FunctionFsRootDir {
391 state: Arc<Mutex<FunctionFsState>>,
392}
393
394impl FunctionFsRootDir {
395 fn create_endpoints(&self, kernel: &Kernel) -> Result<(), Errno> {
396 let mut state = self.state.lock();
397
398 if state.has_input_output_endpoints {
401 return Ok(());
402 }
403 let (device_proxy, adb_proxy, message_counter) =
404 connect_to_device(AdbProxyMode::WakeContainer)?;
405 state.device_proxy = Some(device_proxy);
406
407 let (read_command_sender, read_command_receiver) = async_channel::unbounded();
408 state.adb_read_channel = Some(read_command_sender);
409
410 let (write_command_sender, write_command_receiver) = async_channel::unbounded();
411 state.adb_write_channel = Some(write_command_sender);
412
413 state.event_queue.clear();
414
415 let state_copy = Arc::clone(&self.state);
416 kernel.kthreads.spawn_future(
419 move || async move {
420 let adb_proxy = fadb::UsbAdbImpl_Proxy::new(fidl::AsyncChannel::from_channel(
421 adb_proxy.into_channel(),
422 ));
423 handle_adb(
424 adb_proxy,
425 message_counter,
426 read_command_receiver,
427 write_command_receiver,
428 state_copy,
429 )
430 .await
431 },
432 "functionfs_adb_worker",
433 );
434
435 state.has_input_output_endpoints = true;
436 Ok(())
437 }
438
439 fn from_fs(fs: &FileSystem) -> &Self {
440 fs.root()
441 .node
442 .downcast_ops::<FunctionFsRootDir>()
443 .expect("failed to downcast functionfs root dir")
444 }
445
446 fn from_file(file: &FileObject) -> &Self {
447 Self::from_fs(&file.fs)
448 }
449
450 fn on_control_opened(&self) {
451 let mut state = self.state.lock();
452 state.num_control_file_objects += 1;
453 }
454
455 fn on_control_closed(&self) {
456 let mut state = self.state.lock();
457 state.num_control_file_objects -= 1;
458 if state.num_control_file_objects == 0 {
459 if let Some(device_proxy) = state.device_proxy.as_ref() {
461 let _ = device_proxy
462 .stop_adb(zx::MonotonicInstant::INFINITE)
463 .map_err(|_| errno!(EINVAL));
464 }
465
466 state.has_input_output_endpoints = false;
467 state.is_online = false;
468 state.adb_read_channel = None;
469 state.adb_write_channel = None;
470 }
471 }
472
473 fn wait_until_online(
474 &self,
475 locked: &mut Locked<FileOpsCore>,
476 current_task: &CurrentTask,
477 file: &FileObject,
478 ) -> Result<(), Errno> {
479 loop {
480 let waiter = {
481 let state = self.state.lock();
482 if state.is_online {
483 return Ok(());
484 }
485 if file.flags().contains(OpenFlags::NONBLOCK) {
486 return error!(EAGAIN);
487 }
488 let waiter = Waiter::new();
489 state.waiters.wait_async(&waiter);
490 waiter
491 };
492 waiter.wait(locked, current_task)?;
493 }
494 }
495
496 fn available(&self) -> usize {
497 let state = self.state.lock();
498 state.event_queue.len()
499 }
500
501 fn write(
502 &self,
503 locked: &mut Locked<FileOpsCore>,
504 current_task: &CurrentTask,
505 file: &FileObject,
506 bytes: &[u8],
507 ) -> Result<usize, Errno> {
508 self.wait_until_online(locked, current_task, file)?;
509
510 let data = Vec::from(bytes);
511 let (response_sender, receiver) = std::sync::mpsc::channel();
512 if let Some(channel) = self.state.lock().adb_write_channel.as_ref() {
513 channel
514 .send_blocking(WriteCommand { data, response_sender })
515 .map_err(|_| errno!(EINVAL))?;
516 } else {
517 return error!(ENODEV);
518 }
519 receiver.recv().map_err(|_| errno!(EINVAL))?
520 }
521
522 fn read(
523 &self,
524 locked: &mut Locked<FileOpsCore>,
525 current_task: &CurrentTask,
526 file: &FileObject,
527 ) -> Result<Vec<u8>, Errno> {
528 self.wait_until_online(locked, current_task, file)?;
529
530 let (response_sender, receiver) = std::sync::mpsc::channel();
531 if let Some(channel) = self.state.lock().adb_read_channel.as_ref() {
532 channel.send_blocking(ReadCommand { response_sender }).map_err(|_| errno!(EINVAL))?;
533 } else {
534 return error!(ENODEV);
535 }
536 receiver.recv().map_err(|_| errno!(EINVAL))?
537 }
538}
539
540impl FsNodeOps for FunctionFsRootDir {
541 fs_node_impl_dir_readonly!();
542
543 fn create_file_ops(
544 &self,
545 _locked: &mut Locked<FileOpsCore>,
546 _node: &FsNode,
547 _current_task: &CurrentTask,
548 _flags: OpenFlags,
549 ) -> Result<Box<dyn FileOps>, Errno> {
550 let mut entries = vec![];
551 entries.push(VecDirectoryEntry {
552 entry_type: DirectoryEntryType::REG,
553 name: CONTROL_ENDPOINT.into(),
554 inode: Some(CONTROL_ENDPOINT_NODE_ID),
555 });
556
557 let state = self.state.lock();
558 if state.has_input_output_endpoints {
559 entries.push(VecDirectoryEntry {
560 entry_type: DirectoryEntryType::REG,
561 name: INPUT_ENDPOINT.into(),
562 inode: Some(INPUT_ENDPOINT_NODE_ID),
563 });
564 entries.push(VecDirectoryEntry {
565 entry_type: DirectoryEntryType::REG,
566 name: OUTPUT_ENDPOINT.into(),
567 inode: Some(OUTPUT_ENDPOINT_NODE_ID),
568 });
569 }
570
571 Ok(VecDirectory::new_file(entries))
572 }
573
574 fn lookup(
575 &self,
576 _locked: &mut Locked<FileOpsCore>,
577 node: &FsNode,
578 _current_task: &CurrentTask,
579 name: &FsStr,
580 ) -> Result<starnix_core::vfs::FsNodeHandle, Errno> {
581 let name = std::str::from_utf8(name).map_err(|_| errno!(ENOENT))?;
582 match name {
583 CONTROL_ENDPOINT => Ok(node.fs().create_node(
584 CONTROL_ENDPOINT_NODE_ID,
585 FunctionFsControlEndpoint,
586 FsNodeInfo::new(mode!(IFREG, 0o600), node.info().cred()),
587 )),
588 OUTPUT_ENDPOINT => Ok(node.fs().create_node(
589 OUTPUT_ENDPOINT_NODE_ID,
590 FunctionFsOutputEndpoint,
591 FsNodeInfo::new(mode!(IFREG, 0o600), node.info().cred()),
592 )),
593 INPUT_ENDPOINT => Ok(node.fs().create_node(
594 INPUT_ENDPOINT_NODE_ID,
595 FunctionFsInputEndpoint,
596 FsNodeInfo::new(mode!(IFREG, 0o600), node.info().cred()),
597 )),
598 _ => error!(ENOENT),
599 }
600 }
601}
602
603struct FunctionFsControlEndpoint;
607impl FsNodeOps for FunctionFsControlEndpoint {
608 fs_node_impl_not_dir!();
609
610 fn create_file_ops(
611 &self,
612 _locked: &mut Locked<FileOpsCore>,
613 node: &FsNode,
614 _current_task: &CurrentTask,
615 _flags: OpenFlags,
616 ) -> Result<Box<dyn FileOps>, Errno> {
617 let fs = node.fs();
618 let rootdir = fs
619 .root()
620 .node
621 .downcast_ops::<FunctionFsRootDir>()
622 .expect("failed to downcast functionfs root dir");
623 rootdir.on_control_opened();
624 Ok(Box::new(FunctionFsControlEndpoint))
625 }
626}
627
628impl FileOps for FunctionFsControlEndpoint {
629 fileops_impl_seekless!();
630 fileops_impl_noop_sync!();
631
632 fn close(
633 self: Box<Self>,
634 _locked: &mut Locked<FileOpsCore>,
635 file: &FileObjectState,
636 _current_task: &CurrentTask,
637 ) {
638 let rootdir = FunctionFsRootDir::from_fs(&file.fs);
639 rootdir.on_control_closed();
640 }
641
642 fn read(
643 &self,
644 _locked: &mut Locked<FileOpsCore>,
645 file: &FileObject,
646 _current_task: &CurrentTask,
647 _offset: usize,
648 data: &mut dyn OutputBuffer,
649 ) -> Result<usize, Errno> {
650 track_stub!(
653 TODO("https://fxbug.dev/329699340"),
654 "FunctionFS blocking read on control endpoint"
655 );
656
657 let rootdir = FunctionFsRootDir::from_file(file);
658
659 let mut state = rootdir.state.lock();
660 if !state.event_queue.is_empty() {
661 if data.available() < std::mem::size_of::<usb_functionfs_event>() {
662 return error!(EINVAL);
663 }
664 } else {
665 return error!(EAGAIN);
666 }
667 let front = state.event_queue.pop_front().expect("pop from non-empty event queue");
668 data.write(front.as_bytes())
669 }
670
671 fn write(
672 &self,
673 _locked: &mut Locked<FileOpsCore>,
674 file: &FileObject,
675 current_task: &CurrentTask,
676 _offset: usize,
677 data: &mut dyn InputBuffer,
678 ) -> Result<usize, Errno> {
679 track_stub!(TODO("https://fxbug.dev/329699340"), "FunctionFS should parse descriptors");
683
684 let rootdir = FunctionFsRootDir::from_file(file);
685 rootdir.create_endpoints(current_task.kernel().deref())?;
686
687 Ok(data.drain())
688 }
689
690 fn wait_async(
691 &self,
692 _locked: &mut Locked<FileOpsCore>,
693 file: &FileObject,
694 _current_task: &CurrentTask,
695 waiter: &Waiter,
696 events: FdEvents,
697 handler: EventHandler,
698 ) -> Option<WaitCanceler> {
699 let rootdir = FunctionFsRootDir::from_file(file);
700 let state = rootdir.state.lock();
701 Some(state.waiters.wait_async_fd_events(waiter, events, handler))
702 }
703
704 fn query_events(
705 &self,
706 _locked: &mut Locked<FileOpsCore>,
707 file: &FileObject,
708 _current_task: &CurrentTask,
709 ) -> Result<FdEvents, Errno> {
710 let rootdir = FunctionFsRootDir::from_file(file);
711 if rootdir.available() > 0 { Ok(FdEvents::POLLIN) } else { Ok(FdEvents::empty()) }
712 }
713}
714
715struct FunctionFsInputEndpoint;
718impl FsNodeOps for FunctionFsInputEndpoint {
719 fs_node_impl_not_dir!();
720
721 fn create_file_ops(
722 &self,
723 _locked: &mut Locked<FileOpsCore>,
724 _node: &FsNode,
725 _current_task: &CurrentTask,
726 _flags: OpenFlags,
727 ) -> Result<Box<dyn FileOps>, Errno> {
728 Ok(Box::new(FunctionFsInputEndpoint))
729 }
730}
731
732impl FileOps for FunctionFsInputEndpoint {
733 fileops_impl_seekless!();
734 fileops_impl_noop_sync!();
735
736 fn read(
737 &self,
738 _locked: &mut Locked<FileOpsCore>,
739 _file: &FileObject,
740 _current_task: &CurrentTask,
741 _offset: usize,
742 _data: &mut dyn OutputBuffer,
743 ) -> Result<usize, Errno> {
744 error!(EINVAL)
745 }
746
747 fn write(
748 &self,
749 locked: &mut Locked<FileOpsCore>,
750 file: &FileObject,
751 current_task: &CurrentTask,
752 _offset: usize,
753 data: &mut dyn InputBuffer,
754 ) -> Result<usize, Errno> {
755 let bytes = data.read_all()?;
756 let rootdir = FunctionFsRootDir::from_file(file);
757 rootdir.write(locked, current_task, file, &bytes)
758 }
759}
760
761struct FunctionFsOutputEndpoint;
764impl FsNodeOps for FunctionFsOutputEndpoint {
765 fs_node_impl_not_dir!();
766
767 fn create_file_ops(
768 &self,
769 _locked: &mut Locked<FileOpsCore>,
770 _node: &FsNode,
771 _current_task: &CurrentTask,
772 _flags: OpenFlags,
773 ) -> Result<Box<dyn FileOps>, Errno> {
774 Ok(Box::new(FunctionFsOutputFileObject))
775 }
776}
777
778struct FunctionFsOutputFileObject;
779
780impl FileOps for FunctionFsOutputFileObject {
781 fileops_impl_seekless!();
782 fileops_impl_noop_sync!();
783
784 fn read(
785 &self,
786 locked: &mut Locked<FileOpsCore>,
787 file: &FileObject,
788 current_task: &CurrentTask,
789 _offset: usize,
790 data: &mut dyn OutputBuffer,
791 ) -> Result<usize, Errno> {
792 let rootdir = FunctionFsRootDir::from_file(file);
793 let payload = rootdir.read(locked, current_task, file)?;
794 if payload.len() > data.available() {
795 return error!(EINVAL);
798 }
799
800 data.write(&payload)
801 }
802
803 fn write(
804 &self,
805 _locked: &mut Locked<FileOpsCore>,
806 _file: &FileObject,
807 _current_task: &CurrentTask,
808 _offset: usize,
809 _data: &mut dyn InputBuffer,
810 ) -> Result<usize, Errno> {
811 error!(EINVAL)
812 }
813}