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 if let Some(cond) = err.blocked_condition() {
65 cond.set_active_file(file);
66 }
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 if let Some(cond) = err.blocked_condition() {
138 cond.set_active_file(file);
139 }
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 if offset < 0 {
202 return Err(Errno::EINVAL.into());
203 }
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 if let Some(cond) = err.blocked_condition() {
219 cond.set_active_file(file);
220 }
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 if let Some(cond) = err.blocked_condition() {
351 cond.set_active_file(file);
352 }
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 if let Some(cond) = err.blocked_condition() {
424 cond.set_active_file(file);
425 }
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 if offset < 0 {
488 return Err(Errno::EINVAL.into());
489 }
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 if let Some(cond) = err.blocked_condition() {
505 cond.set_active_file(file);
506 }
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}