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 log_warn!("Failed to call UsbAdbImpl.Receive: {err}");
204 error!(EINVAL)
205 }
206 Ok(Err(err)) => {
207 let status = zx::Status::from_raw(err);
208 if matches!(
209 status,
210 zx::Status::BAD_STATE | zx::Status::CANCELED | zx::Status::PEER_CLOSED
211 ) {
212 log_info!("Receive failed due to connection shutdown: {status}");
213 } else {
214 log_warn!("Failed to receive data from adb driver: {status}");
215 }
216 error!(EINVAL)
219 }
220 Ok(Ok(payload)) => Ok(payload),
221 };
222
223 pending.set_result(response);
224 })
225 .await;
226 }
227
228 async fn handle_write_commands(
230 proxy: &fadb::UsbAdbImpl_Proxy,
231 timeouts_sender: async_channel::Sender<zx::MonotonicInstant>,
232 commands: async_channel::Receiver<WriteCommand>,
233 ) {
234 let timeouts_sender = &timeouts_sender;
235 commands
236 .for_each(|WriteCommand { data, pending }| async move {
237 let response = match proxy.queue_tx(&data).await {
238 Err(err) => {
239 log_warn!("Failed to call UsbAdbImpl.QueueTx: {err}");
240 error!(EINVAL)
241 }
242 Ok(Err(err)) => {
243 log_warn!("Failed to queue data to adb driver: {err}");
244 error!(EINVAL)
245 }
246 Ok(Ok(_)) => Ok(data.len()),
247 };
248
249 timeouts_sender
253 .send(zx::MonotonicInstant::after(ADB_INTERACTION_TIMEOUT))
254 .await
255 .expect("Should be able to send timeout");
256
257 pending.set_result(response);
258 })
259 .await;
260 }
261
262 let (timeouts_sender, timeouts_receiver) = async_channel::unbounded();
263 let event_future = handle_events(proxy.take_event_stream(), &message_counter, state);
264 let write_commands_future =
265 handle_write_commands(&proxy, timeouts_sender.clone(), write_commands);
266 let read_commands_future = handle_read_commands(&proxy, timeouts_sender, read_commands);
267 let timeout_future = handle_idle_timeouts(timeouts_receiver, &message_counter);
268 futures::join!(event_future, write_commands_future, read_commands_future, timeout_future);
269}
270
271pub struct FunctionFs;
272impl FunctionFs {
273 pub fn new_fs(
274 current_task: &CurrentTask,
275 options: FileSystemOptions,
276 ) -> Result<FileSystemHandle, Errno> {
277 if options.source != "adb" {
278 track_stub!(TODO("https://fxbug.dev/329699340"), "FunctionFS supports other uses");
279 return error!(ENODEV);
280 }
281
282 if let Err(e) = std::fs::read_dir(ADB_DIRECTORY) {
286 log_warn!(
287 "Attempted to mount FunctionFS for adb, but could not read {ADB_DIRECTORY}: {e}"
288 );
289 return error!(ENODEV);
290 }
291
292 let uid = if let Some(uid) = options.params.get(b"uid") {
293 fs_args::parse::<uid_t>(uid.as_ref())?
294 } else {
295 0
296 };
297 let gid = if let Some(gid) = options.params.get(b"gid") {
298 fs_args::parse::<gid_t>(gid.as_ref())?
299 } else {
300 0
301 };
302
303 let fs = FileSystem::new(current_task.kernel(), CacheMode::Uncached, FunctionFs, options)?;
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(&self, _fs: &FileSystem, _current_task: &CurrentTask) -> Result<statfs, Errno> {
314 Ok(default_statfs(FUNCTIONFS_MAGIC))
315 }
316
317 fn name(&self) -> &'static FsStr {
318 b"functionfs".into()
319 }
320}
321
322#[derive(Default)]
323struct FunctionFsState {
324 num_control_file_objects: usize,
328
329 has_input_output_endpoints: bool,
332
333 is_online: bool,
335
336 adb_read_channel: Option<async_channel::Sender<ReadCommand>>,
337 adb_write_channel: Option<async_channel::Sender<WriteCommand>>,
338
339 device_proxy: Option<fadb::DeviceSynchronousProxy>,
341
342 event_queue: VecDeque<usb_functionfs_event>,
345
346 waiters: WaitQueue,
347}
348
349pub enum AdbProxyMode {
350 None,
352
353 WakeContainer,
357}
358
359fn connect_to_device(
360 proxy: AdbProxyMode,
361) -> Result<
362 (fadb::DeviceSynchronousProxy, fadb::UsbAdbImpl_SynchronousProxy, Option<zx::Counter>),
363 Errno,
364> {
365 let mut dir = std::fs::read_dir(ADB_DIRECTORY).map_err(|_| errno!(EINVAL))?;
366
367 let Some(Ok(entry)) = dir.next() else {
368 return error!(EBUSY);
369 };
370 let path =
371 entry.path().join("adb").into_os_string().into_string().map_err(|_| errno!(EINVAL))?;
372
373 let (client_channel, server_channel) = zx::Channel::create();
374 fdio::service_connect(&path, server_channel).map_err(|_| errno!(EINVAL))?;
375 let device_proxy = fadb::DeviceSynchronousProxy::new(client_channel);
376
377 let (adb_proxy, server_end) = fidl::endpoints::create_sync_proxy::<fadb::UsbAdbImpl_Marker>();
378 let (adb_proxy, message_counter) = match proxy {
379 AdbProxyMode::None => (adb_proxy, None),
380 AdbProxyMode::WakeContainer => {
381 let (adb_proxy, message_counter) = create_proxy_for_wake_events_counter_zero(
382 adb_proxy.into_channel(),
383 "adb".to_string(),
384 );
385 let adb_proxy = fadb::UsbAdbImpl_SynchronousProxy::from_channel(adb_proxy);
386 (adb_proxy, Some(message_counter))
387 }
388 };
389
390 device_proxy
391 .start_adb(server_end, zx::MonotonicInstant::INFINITE)
392 .map_err(|_| errno!(EINVAL))?
393 .map_err(|_| errno!(EINVAL))?;
394 return Ok((device_proxy, adb_proxy, message_counter));
395}
396
397#[derive(Default)]
398struct FunctionFsRootDir {
399 state: Arc<LockDepMutex<FunctionFsState, FunctionFsStateLock>>,
400}
401
402impl FunctionFsRootDir {
403 fn create_endpoints(&self, kernel: &Kernel) -> Result<(), Errno> {
404 let mut state = self.state.lock();
405
406 if state.has_input_output_endpoints {
409 return Ok(());
410 }
411 let (device_proxy, adb_proxy, message_counter) =
412 connect_to_device(AdbProxyMode::WakeContainer)?;
413 state.device_proxy = Some(device_proxy);
414
415 let (read_command_sender, read_command_receiver) = async_channel::unbounded();
416 state.adb_read_channel = Some(read_command_sender);
417
418 let (write_command_sender, write_command_receiver) = async_channel::unbounded();
419 state.adb_write_channel = Some(write_command_sender);
420
421 state.event_queue.clear();
422
423 let state_copy = Arc::clone(&self.state);
424 kernel.kthreads.spawn_future(
427 move || async move {
428 let adb_proxy = fadb::UsbAdbImpl_Proxy::new(fidl::AsyncChannel::from_channel(
429 adb_proxy.into_channel(),
430 ));
431 handle_adb(
432 adb_proxy,
433 message_counter,
434 read_command_receiver,
435 write_command_receiver,
436 state_copy,
437 )
438 .await
439 },
440 "functionfs_adb_worker",
441 );
442
443 state.has_input_output_endpoints = true;
444 Ok(())
445 }
446
447 fn from_fs(fs: &FileSystem) -> &Self {
448 fs.root()
449 .node
450 .downcast_ops::<FunctionFsRootDir>()
451 .expect("failed to downcast functionfs root dir")
452 }
453
454 fn from_file(file: &FileObject) -> &Self {
455 Self::from_fs(&file.fs)
456 }
457
458 fn on_control_opened(&self) {
459 let mut state = self.state.lock();
460 state.num_control_file_objects += 1;
461 }
462
463 fn on_control_closed(&self) {
464 let mut state = self.state.lock();
465 state.num_control_file_objects -= 1;
466 if state.num_control_file_objects == 0 {
467 if let Some(device_proxy) = state.device_proxy.as_ref() {
469 let _ = device_proxy
470 .stop_adb(zx::MonotonicInstant::INFINITE)
471 .map_err(|_| errno!(EINVAL));
472 }
473
474 state.has_input_output_endpoints = false;
475 state.is_online = false;
476 state.adb_read_channel = None;
477 state.adb_write_channel = None;
478 }
479 }
480
481 fn wait_until_online(
482 &self,
483 current_task: &CurrentTask,
484 file: &FileObject,
485 ) -> Result<(), Errno> {
486 loop {
487 let waiter = {
488 let state = self.state.lock();
489 if state.is_online {
490 return Ok(());
491 }
492 if file.flags().contains(OpenFlags::NONBLOCK) {
493 return error!(EAGAIN);
494 }
495 let waiter = Waiter::new();
496 state.waiters.wait_async(&waiter);
497 waiter
498 };
499 waiter.wait(current_task)?;
500 }
501 }
502
503 fn available(&self) -> usize {
504 let state = self.state.lock();
505 state.event_queue.len()
506 }
507
508 fn write(
509 &self,
510 current_task: &CurrentTask,
511 file: &FileObject,
512 bytes: &[u8],
513 ) -> Result<usize, Errno> {
514 self.wait_until_online(current_task, file)?;
515
516 let data = Vec::from(bytes);
517 let pending = Arc::<PendingResult<usize>>::default();
518 let guard = pending.event.begin_wait();
519
520 if let Some(channel) = self.state.lock().adb_write_channel.as_ref() {
521 channel
522 .send_blocking(WriteCommand { data, pending: pending.clone() })
523 .map_err(|_| errno!(EINVAL))?;
524 } else {
525 return error!(ENODEV);
526 }
527
528 current_task.block_until(guard, zx::MonotonicInstant::INFINITE)?;
529
530 let mut result = pending.result.lock();
531 result.take().ok_or_else(|| errno!(EINTR))?
532 }
533
534 fn read(&self, current_task: &CurrentTask, file: &FileObject) -> Result<Vec<u8>, Errno> {
535 self.wait_until_online(current_task, file)?;
536
537 let pending = Arc::<PendingResult<Vec<u8>>>::default();
538 let guard = pending.event.begin_wait();
539 if let Some(channel) = self.state.lock().adb_read_channel.as_ref() {
540 channel
541 .send_blocking(ReadCommand { pending: pending.clone() })
542 .map_err(|_| errno!(EINVAL))?;
543 } else {
544 return error!(ENODEV);
545 }
546
547 current_task.block_until(guard, zx::MonotonicInstant::INFINITE)?;
548
549 let mut result = pending.result.lock();
550 result.take().ok_or_else(|| errno!(EINTR))?
551 }
552}
553
554impl FsNodeOps for FunctionFsRootDir {
555 fs_node_impl_dir_readonly!();
556
557 fn create_file_ops(
558 &self,
559 _node: &FsNode,
560 _current_task: &CurrentTask,
561 _flags: OpenFlags,
562 ) -> Result<Box<dyn FileOps>, Errno> {
563 let mut entries = vec![];
564 entries.push(VecDirectoryEntry {
565 entry_type: DirectoryEntryType::REG,
566 name: CONTROL_ENDPOINT.into(),
567 inode: Some(CONTROL_ENDPOINT_NODE_ID),
568 });
569
570 let state = self.state.lock();
571 if state.has_input_output_endpoints {
572 entries.push(VecDirectoryEntry {
573 entry_type: DirectoryEntryType::REG,
574 name: INPUT_ENDPOINT.into(),
575 inode: Some(INPUT_ENDPOINT_NODE_ID),
576 });
577 entries.push(VecDirectoryEntry {
578 entry_type: DirectoryEntryType::REG,
579 name: OUTPUT_ENDPOINT.into(),
580 inode: Some(OUTPUT_ENDPOINT_NODE_ID),
581 });
582 }
583
584 Ok(VecDirectory::new_file(entries))
585 }
586
587 fn lookup(
588 &self,
589 node: &FsNode,
590 _current_task: &CurrentTask,
591 name: &FsStr,
592 ) -> Result<starnix_core::vfs::FsNodeHandle, Errno> {
593 let name = std::str::from_utf8(name).map_err(|_| errno!(ENOENT))?;
594 let cred = node.info().cred();
595 match name {
596 CONTROL_ENDPOINT => Ok(node.fs().create_node(
597 CONTROL_ENDPOINT_NODE_ID,
598 FunctionFsControlEndpoint,
599 FsNodeInfo::new(mode!(IFREG, 0o600), cred),
600 )),
601 OUTPUT_ENDPOINT => Ok(node.fs().create_node(
602 OUTPUT_ENDPOINT_NODE_ID,
603 FunctionFsOutputEndpoint,
604 FsNodeInfo::new(mode!(IFREG, 0o600), cred),
605 )),
606 INPUT_ENDPOINT => Ok(node.fs().create_node(
607 INPUT_ENDPOINT_NODE_ID,
608 FunctionFsInputEndpoint,
609 FsNodeInfo::new(mode!(IFREG, 0o600), cred),
610 )),
611 _ => error!(ENOENT),
612 }
613 }
614}
615
616struct FunctionFsControlEndpoint;
620impl FsNodeOps for FunctionFsControlEndpoint {
621 fs_node_impl_not_dir!();
622
623 fn create_file_ops(
624 &self,
625 node: &FsNode,
626 _current_task: &CurrentTask,
627 _flags: OpenFlags,
628 ) -> Result<Box<dyn FileOps>, Errno> {
629 let fs = node.fs();
630 let rootdir = fs
631 .root()
632 .node
633 .downcast_ops::<FunctionFsRootDir>()
634 .expect("failed to downcast functionfs root dir");
635 rootdir.on_control_opened();
636 Ok(Box::new(FunctionFsControlEndpoint))
637 }
638}
639
640impl FileOps for FunctionFsControlEndpoint {
641 fileops_impl_seekless!();
642 fileops_impl_noop_sync!();
643
644 fn close(self: Box<Self>, file: &FileObjectState, _current_task: &CurrentTask) {
645 let rootdir = FunctionFsRootDir::from_fs(&file.fs);
646 rootdir.on_control_closed();
647 }
648
649 fn read(
650 &self,
651 file: &FileObject,
652 _current_task: &CurrentTask,
653 _offset: usize,
654 data: &mut dyn OutputBuffer,
655 ) -> Result<usize, Errno> {
656 track_stub!(
659 TODO("https://fxbug.dev/329699340"),
660 "FunctionFS blocking read on control endpoint"
661 );
662
663 let rootdir = FunctionFsRootDir::from_file(file);
664
665 let mut state = rootdir.state.lock();
666 if !state.event_queue.is_empty() {
667 if data.available() < std::mem::size_of::<usb_functionfs_event>() {
668 return error!(EINVAL);
669 }
670 } else {
671 return error!(EAGAIN);
672 }
673 let front = state.event_queue.pop_front().expect("pop from non-empty event queue");
674 data.write(front.as_bytes())
675 }
676
677 fn write(
678 &self,
679 file: &FileObject,
680 current_task: &CurrentTask,
681 _offset: usize,
682 data: &mut dyn InputBuffer,
683 ) -> Result<usize, Errno> {
684 track_stub!(TODO("https://fxbug.dev/329699340"), "FunctionFS should parse descriptors");
688
689 let rootdir = FunctionFsRootDir::from_file(file);
690 rootdir.create_endpoints(current_task.kernel().deref())?;
691
692 Ok(data.drain())
693 }
694
695 fn wait_async(
696 &self,
697 file: &FileObject,
698 _current_task: &CurrentTask,
699 waiter: &Waiter,
700 events: FdEvents,
701 handler: EventHandler,
702 ) -> Option<WaitCanceler> {
703 let rootdir = FunctionFsRootDir::from_file(file);
704 let state = rootdir.state.lock();
705 Some(state.waiters.wait_async_fd_events(waiter, events, handler))
706 }
707
708 fn query_events(
709 &self,
710 file: &FileObject,
711 _current_task: &CurrentTask,
712 ) -> Result<FdEvents, Errno> {
713 let rootdir = FunctionFsRootDir::from_file(file);
714 if rootdir.available() > 0 { Ok(FdEvents::POLLIN) } else { Ok(FdEvents::empty()) }
715 }
716}
717
718struct FunctionFsInputEndpoint;
721impl FsNodeOps for FunctionFsInputEndpoint {
722 fs_node_impl_not_dir!();
723
724 fn create_file_ops(
725 &self,
726 _node: &FsNode,
727 _current_task: &CurrentTask,
728 _flags: OpenFlags,
729 ) -> Result<Box<dyn FileOps>, Errno> {
730 Ok(Box::new(FunctionFsInputEndpoint))
731 }
732}
733
734impl FileOps for FunctionFsInputEndpoint {
735 fileops_impl_seekless!();
736 fileops_impl_noop_sync!();
737
738 fn read(
739 &self,
740 _file: &FileObject,
741 _current_task: &CurrentTask,
742 _offset: usize,
743 _data: &mut dyn OutputBuffer,
744 ) -> Result<usize, Errno> {
745 error!(EINVAL)
746 }
747
748 fn write(
749 &self,
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(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 _node: &FsNode,
770 _current_task: &CurrentTask,
771 _flags: OpenFlags,
772 ) -> Result<Box<dyn FileOps>, Errno> {
773 Ok(Box::new(FunctionFsOutputFileObject))
774 }
775}
776
777struct FunctionFsOutputFileObject;
778
779impl FileOps for FunctionFsOutputFileObject {
780 fileops_impl_seekless!();
781 fileops_impl_noop_sync!();
782
783 fn read(
784 &self,
785 file: &FileObject,
786 current_task: &CurrentTask,
787 _offset: usize,
788 data: &mut dyn OutputBuffer,
789 ) -> Result<usize, Errno> {
790 let rootdir = FunctionFsRootDir::from_file(file);
791 let payload = rootdir.read(current_task, file)?;
792 if payload.len() > data.available() {
793 return error!(EINVAL);
796 }
797
798 data.write(&payload)
799 }
800
801 fn write(
802 &self,
803 _file: &FileObject,
804 _current_task: &CurrentTask,
805 _offset: usize,
806 _data: &mut dyn InputBuffer,
807 ) -> Result<usize, Errno> {
808 error!(EINVAL)
809 }
810}