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::O_NONBLOCK)
282 {
283 let wait_for = FileState::READABLE;
286
287 debug_assert!(!file.borrow().state().intersects(wait_for));
289
290 return Err(SyscallError::new_blocked_on_file(
291 file.clone(),
292 wait_for,
293 file.borrow().supports_sa_restart(),
294 ));
295 }
296
297 result
298 }
299
300 log_syscall!(
301 writev,
302 libc::ssize_t,
303 std::ffi::c_int,
304 *const libc::iovec,
305 std::ffi::c_int,
306 );
307 pub fn writev(
308 ctx: &mut SyscallContext,
309 fd: std::ffi::c_int,
310 iov_ptr: ForeignPtr<libc::iovec>,
311 iov_count: std::ffi::c_int,
312 ) -> Result<libc::ssize_t, SyscallError> {
313 let file = ctx
316 .objs
317 .thread
318 .syscall_condition()
319 .and_then(|x| x.active_file().cloned());
321
322 let file = match file {
323 Some(x) => x,
325 None => {
327 let desc_table = ctx.objs.thread.descriptor_table_borrow(ctx.objs.host);
328 match Self::get_descriptor(&desc_table, fd)?.file() {
329 CompatFile::New(file) => file.clone(),
330 CompatFile::Legacy(_) => {
332 drop(desc_table);
333 return Self::legacy_syscall(c::syscallhandler_writev, ctx);
334 }
335 }
336 }
337 };
338
339 let iov_count = iov_count.try_into().or(Err(Errno::EINVAL))?;
340
341 let iovs = {
342 let mem = ctx.objs.process.memory_borrow_mut();
343 io::read_iovecs(&mem, iov_ptr, iov_count)?
344 };
345 assert_eq!(iovs.len(), iov_count);
346
347 let mut result = Self::writev_helper(ctx, file.inner_file(), &iovs, None, 0);
348
349 if let Some(err) = result.as_mut().err()
351 && let Some(cond) = err.blocked_condition()
352 {
353 cond.set_active_file(file);
354 }
355
356 let bytes_written = result?;
357 Ok(bytes_written)
358 }
359
360 log_syscall!(
361 pwritev,
362 libc::ssize_t,
363 std::ffi::c_int,
364 *const libc::iovec,
365 std::ffi::c_int,
366 libc::c_ulong,
367 libc::c_ulong,
368 );
369 pub fn pwritev(
370 ctx: &mut SyscallContext,
371 fd: std::ffi::c_int,
372 iov_ptr: ForeignPtr<libc::iovec>,
373 iov_count: std::ffi::c_int,
374 offset_l: libc::c_ulong,
375 _offset_h: libc::c_ulong,
376 ) -> Result<libc::ssize_t, SyscallError> {
377 static_assertions::assert_eq_size!(libc::c_ulong, libc::off_t);
379 let offset = offset_l as libc::off_t;
380
381 let file = ctx
384 .objs
385 .thread
386 .syscall_condition()
387 .and_then(|x| x.active_file().cloned());
389
390 let file = match file {
391 Some(x) => x,
393 None => {
395 let desc_table = ctx.objs.thread.descriptor_table_borrow(ctx.objs.host);
396 match Self::get_descriptor(&desc_table, fd)?.file() {
397 CompatFile::New(file) => file.clone(),
398 CompatFile::Legacy(_) => {
400 drop(desc_table);
401 return Self::legacy_syscall(c::syscallhandler_pwritev, ctx);
402 }
403 }
404 }
405 };
406
407 if offset < 0 {
409 return Err(Errno::EINVAL.into());
410 }
411
412 let iov_count = iov_count.try_into().or(Err(Errno::EINVAL))?;
413
414 let iovs = {
415 let mem = ctx.objs.process.memory_borrow_mut();
416 io::read_iovecs(&mem, iov_ptr, iov_count)?
417 };
418 assert_eq!(iovs.len(), iov_count);
419
420 let mut result = Self::writev_helper(ctx, file.inner_file(), &iovs, Some(offset), 0);
421
422 if let Some(err) = result.as_mut().err()
424 && let Some(cond) = err.blocked_condition()
425 {
426 cond.set_active_file(file);
427 }
428
429 let bytes_written = result?;
430 Ok(bytes_written)
431 }
432
433 log_syscall!(
434 pwritev2,
435 libc::ssize_t,
436 std::ffi::c_int,
437 *const libc::iovec,
438 std::ffi::c_int,
439 libc::c_ulong,
440 libc::c_ulong,
441 std::ffi::c_int,
442 );
443 pub fn pwritev2(
444 ctx: &mut SyscallContext,
445 fd: std::ffi::c_int,
446 iov_ptr: ForeignPtr<libc::iovec>,
447 iov_count: std::ffi::c_int,
448 offset_l: libc::c_ulong,
449 _offset_h: libc::c_ulong,
450 flags: std::ffi::c_int,
451 ) -> Result<libc::ssize_t, SyscallError> {
452 static_assertions::assert_eq_size!(libc::c_ulong, libc::off_t);
454 let offset = offset_l as libc::off_t;
455
456 let file = ctx
459 .objs
460 .thread
461 .syscall_condition()
462 .and_then(|x| x.active_file().cloned());
464
465 let file = match file {
466 Some(x) => x,
468 None => {
470 let desc_table = ctx.objs.thread.descriptor_table_borrow(ctx.objs.host);
471 match Self::get_descriptor(&desc_table, fd)?.file() {
472 CompatFile::New(file) => file.clone(),
473 CompatFile::Legacy(_) => {
475 drop(desc_table);
476 return Self::legacy_syscall(c::syscallhandler_pwritev2, ctx);
477 }
478 }
479 }
480 };
481
482 let offset = (offset != -1).then_some(offset);
485
486 if let Some(offset) = offset
488 && offset < 0
489 {
490 return Err(Errno::EINVAL.into());
491 }
492
493 let iov_count = iov_count.try_into().or(Err(Errno::EINVAL))?;
494
495 let iovs = {
496 let mem = ctx.objs.process.memory_borrow_mut();
497 io::read_iovecs(&mem, iov_ptr, iov_count)?
498 };
499 assert_eq!(iovs.len(), iov_count);
500
501 let mut result = Self::writev_helper(ctx, file.inner_file(), &iovs, offset, flags);
502
503 if let Some(err) = result.as_mut().err()
505 && let Some(cond) = err.blocked_condition()
506 {
507 cond.set_active_file(file);
508 }
509
510 let bytes_written = result?;
511 Ok(bytes_written)
512 }
513
514 pub fn writev_helper(
515 ctx: &mut SyscallContext,
516 file: &File,
517 iovs: &[IoVec],
518 offset: Option<libc::off_t>,
519 flags: std::ffi::c_int,
520 ) -> Result<libc::ssize_t, SyscallError> {
521 let mut mem = ctx.objs.process.memory_borrow_mut();
522 let mut rng = ctx.objs.host.random_mut();
523 let net_ns = ctx.objs.host.network_namespace_borrow();
524
525 if let File::Socket(socket) = file {
527 if offset.is_some() {
528 return Err(Errno::ESPIPE.into());
530 }
531
532 let args = SendmsgArgs {
533 addr: None,
534 iovs,
535 control_ptr: ForeignArrayPtr::new(ForeignPtr::null(), 0),
536 flags: 0,
537 };
538
539 let bytes_written = CallbackQueue::queue_and_run_with_legacy(|cb_queue| {
541 Socket::sendmsg(socket, args, &mut mem, &net_ns, &mut *rng, cb_queue)
542 })?;
543
544 return Ok(bytes_written);
545 }
546
547 let file_status = file.borrow().status();
548
549 let result =
550 CallbackQueue::queue_and_run_with_legacy(|cb_queue| {
552 file.borrow_mut().writev(
553 iovs,
554 offset,
555 flags,
556 &mut mem,
557 cb_queue,
558 )
559 });
560
561 if result == Err(Errno::EWOULDBLOCK.into()) && !file_status.contains(FileStatus::O_NONBLOCK)
563 {
564 let wait_for = FileState::WRITABLE;
567
568 debug_assert!(!file.borrow().state().intersects(wait_for));
570
571 return Err(SyscallError::new_blocked_on_file(
572 file.clone(),
573 wait_for,
574 file.borrow().supports_sa_restart(),
575 ));
576 }
577
578 result
579 }
580}