1use linux_api::errno::Errno;
2use shadow_shim_helper_rs::syscall_types::ForeignPtr;
3
4use crate::cshadow as c;
5use crate::host::descriptor::socket::{RecvmsgArgs, RecvmsgReturn, SendmsgArgs, Socket};
6use crate::host::descriptor::{CompatFile, File, FileState, FileStatus};
7use crate::host::syscall::handler::{SyscallContext, SyscallHandler};
8use crate::host::syscall::io::{self, IoVec};
9use crate::host::syscall::types::{ForeignArrayPtr, SyscallError};
10use crate::utility::callback_queue::CallbackQueue;
11
12impl SyscallHandler {
13    log_syscall!(
14        readv,
15        libc::ssize_t,
16        std::ffi::c_int,
17        *const libc::iovec,
18        std::ffi::c_int,
19    );
20    pub fn readv(
21        ctx: &mut SyscallContext,
22        fd: std::ffi::c_int,
23        iov_ptr: ForeignPtr<libc::iovec>,
24        iov_count: std::ffi::c_int,
25    ) -> Result<libc::ssize_t, SyscallError> {
26        let file = ctx
29            .objs
30            .thread
31            .syscall_condition()
32            .and_then(|x| x.active_file().cloned());
34
35        let file = match file {
36            Some(x) => x,
38            None => {
40                let desc_table = ctx.objs.thread.descriptor_table_borrow(ctx.objs.host);
41                match Self::get_descriptor(&desc_table, fd)?.file() {
42                    CompatFile::New(file) => file.clone(),
43                    CompatFile::Legacy(_) => {
45                        drop(desc_table);
46                        return Self::legacy_syscall(c::syscallhandler_readv, ctx);
47                    }
48                }
49            }
50        };
51
52        let iov_count = iov_count.try_into().or(Err(Errno::EINVAL))?;
53
54        let iovs = {
55            let mem = ctx.objs.process.memory_borrow_mut();
56            io::read_iovecs(&mem, iov_ptr, iov_count)?
57        };
58        assert_eq!(iovs.len(), iov_count);
59
60        let mut result = Self::readv_helper(ctx, file.inner_file(), &iovs, None, 0);
61
62        if let Some(err) = result.as_mut().err()
64            && let Some(cond) = err.blocked_condition()
65        {
66            cond.set_active_file(file);
67        }
68
69        let bytes_read = result?;
70        Ok(bytes_read)
71    }
72
73    log_syscall!(
74        preadv,
75        libc::ssize_t,
76        std::ffi::c_int,
77        *const libc::iovec,
78        std::ffi::c_int,
79        libc::c_ulong,
80        libc::c_ulong,
81    );
82    pub fn preadv(
83        ctx: &mut SyscallContext,
84        fd: std::ffi::c_int,
85        iov_ptr: ForeignPtr<libc::iovec>,
86        iov_count: std::ffi::c_int,
87        offset_l: libc::c_ulong,
88        _offset_h: libc::c_ulong,
89    ) -> Result<libc::ssize_t, SyscallError> {
90        static_assertions::assert_eq_size!(libc::c_ulong, libc::off_t);
92        let offset = offset_l as libc::off_t;
93
94        let file = ctx
97            .objs
98            .thread
99            .syscall_condition()
100            .and_then(|x| x.active_file().cloned());
102
103        let file = match file {
104            Some(x) => x,
106            None => {
108                let desc_table = ctx.objs.thread.descriptor_table_borrow(ctx.objs.host);
109                match Self::get_descriptor(&desc_table, fd)?.file() {
110                    CompatFile::New(file) => file.clone(),
111                    CompatFile::Legacy(_) => {
113                        drop(desc_table);
114                        return Self::legacy_syscall(c::syscallhandler_preadv, ctx);
115                    }
116                }
117            }
118        };
119
120        if offset < 0 {
122            return Err(Errno::EINVAL.into());
123        }
124
125        let iov_count = iov_count.try_into().or(Err(Errno::EINVAL))?;
126
127        let iovs = {
128            let mem = ctx.objs.process.memory_borrow_mut();
129            io::read_iovecs(&mem, iov_ptr, iov_count)?
130        };
131        assert_eq!(iovs.len(), iov_count);
132
133        let mut result = Self::readv_helper(ctx, file.inner_file(), &iovs, Some(offset), 0);
134
135        if let Some(err) = result.as_mut().err()
137            && let Some(cond) = err.blocked_condition()
138        {
139            cond.set_active_file(file);
140        }
141
142        let bytes_read = result?;
143        Ok(bytes_read)
144    }
145
146    log_syscall!(
147        preadv2,
148        libc::ssize_t,
149        std::ffi::c_int,
150        *const libc::iovec,
151        std::ffi::c_int,
152        libc::c_ulong,
153        libc::c_ulong,
154        std::ffi::c_int,
155    );
156    pub fn preadv2(
157        ctx: &mut SyscallContext,
158        fd: std::ffi::c_int,
159        iov_ptr: ForeignPtr<libc::iovec>,
160        iov_count: std::ffi::c_int,
161        offset_l: libc::c_ulong,
162        _offset_h: libc::c_ulong,
163        flags: std::ffi::c_int,
164    ) -> Result<libc::ssize_t, SyscallError> {
165        static_assertions::assert_eq_size!(libc::c_ulong, libc::off_t);
167        let offset = offset_l as libc::off_t;
168
169        let file = ctx
172            .objs
173            .thread
174            .syscall_condition()
175            .and_then(|x| x.active_file().cloned());
177
178        let file = match file {
179            Some(x) => x,
181            None => {
183                let desc_table = ctx.objs.thread.descriptor_table_borrow(ctx.objs.host);
184                match Self::get_descriptor(&desc_table, fd)?.file() {
185                    CompatFile::New(file) => file.clone(),
186                    CompatFile::Legacy(_) => {
188                        drop(desc_table);
189                        return Self::legacy_syscall(c::syscallhandler_preadv2, ctx);
190                    }
191                }
192            }
193        };
194
195        let offset = (offset != -1).then_some(offset);
198
199        if let Some(offset) = offset
201            && offset < 0
202        {
203            return Err(Errno::EINVAL.into());
204        }
205
206        let iov_count = iov_count.try_into().or(Err(Errno::EINVAL))?;
207
208        let iovs = {
209            let mem = ctx.objs.process.memory_borrow_mut();
210            io::read_iovecs(&mem, iov_ptr, iov_count)?
211        };
212        assert_eq!(iovs.len(), iov_count);
213
214        let mut result = Self::readv_helper(ctx, file.inner_file(), &iovs, offset, flags);
215
216        if let Some(err) = result.as_mut().err()
218            && let Some(cond) = err.blocked_condition()
219        {
220            cond.set_active_file(file);
221        }
222
223        let bytes_read = result?;
224        Ok(bytes_read)
225    }
226
227    pub fn readv_helper(
228        ctx: &mut SyscallContext,
229        file: &File,
230        iovs: &[IoVec],
231        offset: Option<libc::off_t>,
232        flags: std::ffi::c_int,
233    ) -> Result<libc::ssize_t, SyscallError> {
234        let mut mem = ctx.objs.process.memory_borrow_mut();
235
236        if let File::Socket(socket) = file {
238            if offset.is_some() {
239                return Err(Errno::ESPIPE.into());
241            }
242
243            if iovs.iter().map(|x| x.len).sum::<usize>() == 0 {
248                return Ok(0);
249            }
250
251            let args = RecvmsgArgs {
252                iovs,
253                control_ptr: ForeignArrayPtr::new(ForeignPtr::null(), 0),
254                flags: 0,
255            };
256
257            let RecvmsgReturn { return_val, .. } =
259                CallbackQueue::queue_and_run_with_legacy(|cb_queue| {
260                    Socket::recvmsg(socket, args, &mut mem, cb_queue)
261                })?;
262
263            return Ok(return_val);
264        }
265
266        let file_status = file.borrow().status();
267
268        let result =
269            CallbackQueue::queue_and_run_with_legacy(|cb_queue| {
271                file.borrow_mut().readv(
272                    iovs,
273                    offset,
274                    flags,
275                    &mut mem,
276                    cb_queue,
277                )
278            });
279
280        if result == Err(Errno::EWOULDBLOCK.into()) && !file_status.contains(FileStatus::NONBLOCK) {
282            let wait_for = FileState::READABLE;
285
286            debug_assert!(!file.borrow().state().intersects(wait_for));
288
289            return Err(SyscallError::new_blocked_on_file(
290                file.clone(),
291                wait_for,
292                file.borrow().supports_sa_restart(),
293            ));
294        }
295
296        result
297    }
298
299    log_syscall!(
300        writev,
301        libc::ssize_t,
302        std::ffi::c_int,
303        *const libc::iovec,
304        std::ffi::c_int,
305    );
306    pub fn writev(
307        ctx: &mut SyscallContext,
308        fd: std::ffi::c_int,
309        iov_ptr: ForeignPtr<libc::iovec>,
310        iov_count: std::ffi::c_int,
311    ) -> Result<libc::ssize_t, SyscallError> {
312        let file = ctx
315            .objs
316            .thread
317            .syscall_condition()
318            .and_then(|x| x.active_file().cloned());
320
321        let file = match file {
322            Some(x) => x,
324            None => {
326                let desc_table = ctx.objs.thread.descriptor_table_borrow(ctx.objs.host);
327                match Self::get_descriptor(&desc_table, fd)?.file() {
328                    CompatFile::New(file) => file.clone(),
329                    CompatFile::Legacy(_) => {
331                        drop(desc_table);
332                        return Self::legacy_syscall(c::syscallhandler_writev, ctx);
333                    }
334                }
335            }
336        };
337
338        let iov_count = iov_count.try_into().or(Err(Errno::EINVAL))?;
339
340        let iovs = {
341            let mem = ctx.objs.process.memory_borrow_mut();
342            io::read_iovecs(&mem, iov_ptr, iov_count)?
343        };
344        assert_eq!(iovs.len(), iov_count);
345
346        let mut result = Self::writev_helper(ctx, file.inner_file(), &iovs, None, 0);
347
348        if let Some(err) = result.as_mut().err()
350            && let Some(cond) = err.blocked_condition()
351        {
352            cond.set_active_file(file);
353        }
354
355        let bytes_written = result?;
356        Ok(bytes_written)
357    }
358
359    log_syscall!(
360        pwritev,
361        libc::ssize_t,
362        std::ffi::c_int,
363        *const libc::iovec,
364        std::ffi::c_int,
365        libc::c_ulong,
366        libc::c_ulong,
367    );
368    pub fn pwritev(
369        ctx: &mut SyscallContext,
370        fd: std::ffi::c_int,
371        iov_ptr: ForeignPtr<libc::iovec>,
372        iov_count: std::ffi::c_int,
373        offset_l: libc::c_ulong,
374        _offset_h: libc::c_ulong,
375    ) -> Result<libc::ssize_t, SyscallError> {
376        static_assertions::assert_eq_size!(libc::c_ulong, libc::off_t);
378        let offset = offset_l as libc::off_t;
379
380        let file = ctx
383            .objs
384            .thread
385            .syscall_condition()
386            .and_then(|x| x.active_file().cloned());
388
389        let file = match file {
390            Some(x) => x,
392            None => {
394                let desc_table = ctx.objs.thread.descriptor_table_borrow(ctx.objs.host);
395                match Self::get_descriptor(&desc_table, fd)?.file() {
396                    CompatFile::New(file) => file.clone(),
397                    CompatFile::Legacy(_) => {
399                        drop(desc_table);
400                        return Self::legacy_syscall(c::syscallhandler_pwritev, ctx);
401                    }
402                }
403            }
404        };
405
406        if offset < 0 {
408            return Err(Errno::EINVAL.into());
409        }
410
411        let iov_count = iov_count.try_into().or(Err(Errno::EINVAL))?;
412
413        let iovs = {
414            let mem = ctx.objs.process.memory_borrow_mut();
415            io::read_iovecs(&mem, iov_ptr, iov_count)?
416        };
417        assert_eq!(iovs.len(), iov_count);
418
419        let mut result = Self::writev_helper(ctx, file.inner_file(), &iovs, Some(offset), 0);
420
421        if let Some(err) = result.as_mut().err()
423            && let Some(cond) = err.blocked_condition()
424        {
425            cond.set_active_file(file);
426        }
427
428        let bytes_written = result?;
429        Ok(bytes_written)
430    }
431
432    log_syscall!(
433        pwritev2,
434        libc::ssize_t,
435        std::ffi::c_int,
436        *const libc::iovec,
437        std::ffi::c_int,
438        libc::c_ulong,
439        libc::c_ulong,
440        std::ffi::c_int,
441    );
442    pub fn pwritev2(
443        ctx: &mut SyscallContext,
444        fd: std::ffi::c_int,
445        iov_ptr: ForeignPtr<libc::iovec>,
446        iov_count: std::ffi::c_int,
447        offset_l: libc::c_ulong,
448        _offset_h: libc::c_ulong,
449        flags: std::ffi::c_int,
450    ) -> Result<libc::ssize_t, SyscallError> {
451        static_assertions::assert_eq_size!(libc::c_ulong, libc::off_t);
453        let offset = offset_l as libc::off_t;
454
455        let file = ctx
458            .objs
459            .thread
460            .syscall_condition()
461            .and_then(|x| x.active_file().cloned());
463
464        let file = match file {
465            Some(x) => x,
467            None => {
469                let desc_table = ctx.objs.thread.descriptor_table_borrow(ctx.objs.host);
470                match Self::get_descriptor(&desc_table, fd)?.file() {
471                    CompatFile::New(file) => file.clone(),
472                    CompatFile::Legacy(_) => {
474                        drop(desc_table);
475                        return Self::legacy_syscall(c::syscallhandler_pwritev2, ctx);
476                    }
477                }
478            }
479        };
480
481        let offset = (offset != -1).then_some(offset);
484
485        if let Some(offset) = offset
487            && offset < 0
488        {
489            return Err(Errno::EINVAL.into());
490        }
491
492        let iov_count = iov_count.try_into().or(Err(Errno::EINVAL))?;
493
494        let iovs = {
495            let mem = ctx.objs.process.memory_borrow_mut();
496            io::read_iovecs(&mem, iov_ptr, iov_count)?
497        };
498        assert_eq!(iovs.len(), iov_count);
499
500        let mut result = Self::writev_helper(ctx, file.inner_file(), &iovs, offset, flags);
501
502        if let Some(err) = result.as_mut().err()
504            && let Some(cond) = err.blocked_condition()
505        {
506            cond.set_active_file(file);
507        }
508
509        let bytes_written = result?;
510        Ok(bytes_written)
511    }
512
513    pub fn writev_helper(
514        ctx: &mut SyscallContext,
515        file: &File,
516        iovs: &[IoVec],
517        offset: Option<libc::off_t>,
518        flags: std::ffi::c_int,
519    ) -> Result<libc::ssize_t, SyscallError> {
520        let mut mem = ctx.objs.process.memory_borrow_mut();
521        let mut rng = ctx.objs.host.random_mut();
522        let net_ns = ctx.objs.host.network_namespace_borrow();
523
524        if let File::Socket(socket) = file {
526            if offset.is_some() {
527                return Err(Errno::ESPIPE.into());
529            }
530
531            let args = SendmsgArgs {
532                addr: None,
533                iovs,
534                control_ptr: ForeignArrayPtr::new(ForeignPtr::null(), 0),
535                flags: 0,
536            };
537
538            let bytes_written = CallbackQueue::queue_and_run_with_legacy(|cb_queue| {
540                Socket::sendmsg(socket, args, &mut mem, &net_ns, &mut *rng, cb_queue)
541            })?;
542
543            return Ok(bytes_written);
544        }
545
546        let file_status = file.borrow().status();
547
548        let result =
549            CallbackQueue::queue_and_run_with_legacy(|cb_queue| {
551                file.borrow_mut().writev(
552                    iovs,
553                    offset,
554                    flags,
555                    &mut mem,
556                    cb_queue,
557                )
558            });
559
560        if result == Err(Errno::EWOULDBLOCK.into()) && !file_status.contains(FileStatus::NONBLOCK) {
562            let wait_for = FileState::WRITABLE;
565
566            debug_assert!(!file.borrow().state().intersects(wait_for));
568
569            return Err(SyscallError::new_blocked_on_file(
570                file.clone(),
571                wait_for,
572                file.borrow().supports_sa_restart(),
573            ));
574        }
575
576        result
577    }
578}