shadow_rs/host/syscall/handler/
close_range.rs

1use linux_api::close_range::CloseRangeFlags;
2use linux_api::errno::Errno;
3use linux_api::fcntl::DescriptorFlags;
4
5use crate::host::descriptor::descriptor_table;
6use crate::host::syscall::handler::{SyscallContext, SyscallHandler};
7use crate::utility::callback_queue::CallbackQueue;
8
9impl SyscallHandler {
10    log_syscall!(
11        close_range,
12        /* rv */ std::ffi::c_int,
13        /* first */ std::ffi::c_uint,
14        /* last */ std::ffi::c_uint,
15        /* flags */ CloseRangeFlags,
16    );
17    pub fn close_range(
18        ctx: &mut SyscallContext,
19        first: std::ffi::c_uint,
20        last: std::ffi::c_uint,
21        flags: std::ffi::c_uint,
22    ) -> Result<(), Errno> {
23        // close_range(2):
24        // > EINVAL: [...], or first is greater than last.
25        if first > last {
26            return Err(Errno::EINVAL);
27        }
28
29        // if the start of the range is larger than the max possible fd, then do nothing
30        if first > descriptor_table::FD_MAX {
31            return Ok(());
32        }
33
34        // restrict the end of the range to the max possible fd
35        let last = std::cmp::min(last, descriptor_table::FD_MAX);
36
37        // we ensured above that first and last are within the allowed fd range
38        let first = first.try_into().unwrap();
39        let last = last.try_into().unwrap();
40
41        let range = first..=last;
42
43        let Some(flags) = CloseRangeFlags::from_bits(flags) else {
44            log::debug!("Invalid close_range flags: {flags}");
45            return Err(Errno::EINVAL);
46        };
47
48        if flags.contains(CloseRangeFlags::CLOSE_RANGE_UNSHARE) {
49            log::debug!("The CLOSE_RANGE_UNSHARE flag is not implemented");
50            return Err(Errno::EINVAL);
51        }
52
53        let mut desc_table = ctx.objs.thread.descriptor_table_borrow_mut(ctx.objs.host);
54
55        if flags.contains(CloseRangeFlags::CLOSE_RANGE_CLOEXEC) {
56            // close_range(2):
57            // > CLOSE_RANGE_CLOEXEC: Set the close-on-exec flag on the specified file descriptors,
58            // > rather than immediately closing them.
59
60            // set the CLOEXEC flag on all descriptors in the range
61            for (fd, desc) in desc_table.iter_mut() {
62                if range.contains(fd) {
63                    desc.set_flags(desc.flags() | DescriptorFlags::FD_CLOEXEC);
64                }
65            }
66        } else {
67            // remove all descriptors in the range
68            let descriptors = desc_table.remove_range(range);
69
70            // close the removed descriptors
71            CallbackQueue::queue_and_run_with_legacy(|cb_queue| {
72                for desc in descriptors {
73                    // close_range(2):
74                    // > Errors closing a given file descriptor are currently ignored.
75                    let _ = desc.close(ctx.objs.host, cb_queue);
76                }
77            });
78        }
79
80        Ok(())
81    }
82}