88//! I/O host functions for WebAssembly modules
99
1010use crate :: wasm:: Linker ;
11+ use crate :: wasm:: func:: Caller ;
12+ use super :: mem_access:: MemoryAccessor ;
13+ use alloc:: string:: String ;
1114
1215/// IoVec structure for scatter-gather I/O
1316#[ repr( C ) ]
@@ -32,10 +35,10 @@ pub const ERRNO_NOSYS: i32 = 52; // Function not implemented
3235
3336pub fn register < T > ( linker : & mut Linker < T > ) -> crate :: Result < ( ) > {
3437 // fd_write - Write to a file descriptor
35- linker. func_wrap (
38+ linker. func_wrap_with_memory (
3639 "wasi_snapshot_preview1" ,
3740 "fd_write" ,
38- |fd : i32 , iovs_ptr : i32 , iovs_len : i32 , nwritten_ptr : i32 | -> i32 {
41+ |mut caller : Caller < ' _ , T > , fd : i32 , iovs_ptr : i32 , iovs_len : i32 , nwritten_ptr : i32 | -> i32 {
3942 // Validate parameters first
4043 if iovs_ptr < 0 || iovs_len < 0 || nwritten_ptr < 0 {
4144 return ERRNO_INVAL ;
@@ -44,27 +47,60 @@ pub fn register<T>(linker: &mut Linker<T>) -> crate::Result<()> {
4447 // Check if fd is valid: stdout (1), stderr (2), or reasonable file descriptors (3-63)
4548 // Invalid: stdin (0), negative values, or unreasonably high values
4649 if fd == FD_STDOUT || fd == FD_STDERR || ( fd >= 3 && fd < 64 ) {
47- // Valid fd - log and proceed
48- if fd == FD_STDOUT || fd == FD_STDERR {
49- let prefix = if fd == FD_STDOUT { "stdout" } else { "stderr" } ;
50- tracing:: debug!( "[WASM {}] fd_write called with {} iovecs" , prefix, iovs_len) ;
50+ // Get memory accessor
51+ let mem = match MemoryAccessor :: new ( & mut caller) {
52+ Some ( m) => m,
53+ None => return ERRNO_INVAL ,
54+ } ;
55+
56+ let mut total_written = 0u32 ;
57+
58+ // Read IoVec array from WASM memory
59+ for i in 0 ..iovs_len {
60+ let iov_offset = iovs_ptr as u32 + ( i as u32 * 8 ) ; // Each IoVec is 8 bytes
5161
52- // For demonstration, log that we would output data
53- // In a real implementation with memory access, we would read the IoVecs
54- // and output the actual data
55- tracing:: info!( "[WASM {}] Writing {} iovecs" , prefix, iovs_len) ;
56- } else {
57- tracing:: debug!( "[WASM] fd_write called for fd={} with {} iovecs" , fd, iovs_len) ;
62+ // Read IoVec structure
63+ let buf_ptr = match unsafe { mem. read :: < u32 > ( iov_offset) } {
64+ Some ( ptr) => ptr,
65+ None => return ERRNO_INVAL ,
66+ } ;
67+
68+ let buf_len = match unsafe { mem. read :: < u32 > ( iov_offset + 4 ) } {
69+ Some ( len) => len,
70+ None => return ERRNO_INVAL ,
71+ } ;
72+
73+ // Read actual data from WASM memory
74+ if buf_len > 0 {
75+ let data = match mem. read_bytes ( buf_ptr, buf_len) {
76+ Some ( d) => d,
77+ None => return ERRNO_INVAL ,
78+ } ;
79+
80+ // Output to console based on fd
81+ if fd == FD_STDOUT || fd == FD_STDERR {
82+ let output = String :: from_utf8_lossy ( & data) ;
83+ // Use distinct targets and levels to differentiate streams
84+ if fd == FD_STDOUT {
85+ tracing:: info!( target: "wasi::stdout" , "{}" , output) ;
86+ } else {
87+ tracing:: error!( target: "wasi::stderr" , "{}" , output) ;
88+ }
89+ } else {
90+ // For file descriptors, just count bytes (stub)
91+ tracing:: debug!( "[WASM] fd_write to fd={}: {} bytes" , fd, buf_len) ;
92+ }
93+
94+ total_written += buf_len;
95+ }
5896 }
5997
60- // TODO: When we have proper memory access:
61- // 1. Read IoVec array from WASM memory at iovs_ptr
62- // 2. For each IoVec, read the actual data buffer
63- // 3. Output the data to console or file
64- // 4. Write total bytes written to nwritten_ptr
65-
66- // For now, return success
67- ERRNO_SUCCESS
98+ // Write bytes written count to nwritten_ptr
99+ if unsafe { mem. write :: < u32 > ( nwritten_ptr as u32 , & total_written) } {
100+ ERRNO_SUCCESS
101+ } else {
102+ ERRNO_INVAL
103+ }
68104 } else {
69105 // Invalid fd (includes stdin and any other invalid values)
70106 ERRNO_BADF
@@ -73,10 +109,10 @@ pub fn register<T>(linker: &mut Linker<T>) -> crate::Result<()> {
73109 ) ?;
74110
75111 // fd_read - Read from a file descriptor
76- linker. func_wrap (
112+ linker. func_wrap_with_memory (
77113 "wasi_snapshot_preview1" ,
78114 "fd_read" ,
79- |fd : i32 , iovs_ptr : i32 , iovs_len : i32 , nread_ptr : i32 | -> i32 {
115+ |mut caller : Caller < ' _ , T > , fd : i32 , iovs_ptr : i32 , iovs_len : i32 , nread_ptr : i32 | -> i32 {
80116 // Can't read from stdout/stderr
81117 if fd == FD_STDOUT || fd == FD_STDERR {
82118 return ERRNO_BADF ;
@@ -92,17 +128,27 @@ pub fn register<T>(linker: &mut Linker<T>) -> crate::Result<()> {
92128 return ERRNO_INVAL ;
93129 }
94130
95- // TODO: Implement actual reading from console
96- // For now, return 0 bytes read (EOF)
131+ // Get memory accessor
132+ let mem = match MemoryAccessor :: new ( & mut caller) {
133+ Some ( m) => m,
134+ None => return ERRNO_INVAL ,
135+ } ;
136+
137+ // For stub implementation, always return EOF (0 bytes read)
138+ let total_read = 0u32 ;
139+
97140 if fd == FD_STDIN {
98- tracing:: debug!( "[WASM stdin] fd_read called" ) ;
141+ tracing:: debug!( "[WASM stdin] fd_read called, returning EOF " ) ;
99142 } else {
100- tracing:: debug!( "[WASM] fd_read called for fd={}" , fd) ;
143+ tracing:: debug!( "[WASM] fd_read called for fd={}, returning EOF" , fd) ;
144+ }
145+
146+ // Write 0 to nread_ptr to indicate EOF
147+ if unsafe { mem. write :: < u32 > ( nread_ptr as u32 , & total_read) } {
148+ ERRNO_SUCCESS
149+ } else {
150+ ERRNO_INVAL
101151 }
102-
103- // TODO: Write 0 to nread_ptr to indicate EOF
104- // For now, just return success
105- ERRNO_SUCCESS
106152 } ,
107153 ) ?;
108154
@@ -124,15 +170,31 @@ pub fn register<T>(linker: &mut Linker<T>) -> crate::Result<()> {
124170 } ) ?;
125171
126172 // fd_seek - Seek in a file
127- linker. func_wrap (
173+ linker. func_wrap_with_memory (
128174 "wasi_snapshot_preview1" ,
129175 "fd_seek" ,
130- |fd : i32 , offset : i64 , whence : i32 , _newoffset_ptr : i32 | -> i32 {
176+ |mut caller : Caller < ' _ , T > , fd : i32 , offset : i64 , whence : i32 , newoffset_ptr : i32 | -> i32 {
131177 tracing:: debug!( "[WASM] fd_seek({}, {}, {})" , fd, offset, whence) ;
132178
133- // For stub, just return success
134- // TODO: Write new position to newoffset_ptr
135- ERRNO_SUCCESS
179+ if newoffset_ptr < 0 {
180+ return ERRNO_INVAL ;
181+ }
182+
183+ // Get memory accessor
184+ let mem = match MemoryAccessor :: new ( & mut caller) {
185+ Some ( m) => m,
186+ None => return ERRNO_INVAL ,
187+ } ;
188+
189+ // For stub, just return the requested offset as new position
190+ let new_offset = offset as u64 ;
191+
192+ // Write new position to newoffset_ptr
193+ if unsafe { mem. write :: < u64 > ( newoffset_ptr as u32 , & new_offset) } {
194+ ERRNO_SUCCESS
195+ } else {
196+ ERRNO_INVAL
197+ }
136198 } ,
137199 ) ?;
138200
0 commit comments