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