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