shadow_rs/host/syscall/handler/
fcntl.rs
1use linux_api::errno::Errno;
2use linux_api::fcntl::{DescriptorFlags, FcntlCommand, OFlag};
3use log::debug;
4
5use crate::cshadow;
6use crate::host::descriptor::{CompatFile, File, FileStatus};
7use crate::host::syscall::handler::{SyscallContext, SyscallHandler};
8use crate::host::syscall::type_formatting::SyscallNonDeterministicArg;
9use crate::host::syscall::types::SyscallError;
10
11impl SyscallHandler {
12 log_syscall!(
13 fcntl,
14 std::ffi::c_long,
15 std::ffi::c_uint,
16 std::ffi::c_uint,
17 SyscallNonDeterministicArg<std::ffi::c_ulong>,
18 );
19 pub fn fcntl(
20 ctx: &mut SyscallContext,
21 fd: std::ffi::c_uint,
22 cmd: std::ffi::c_uint,
23 arg: std::ffi::c_ulong,
24 ) -> Result<std::ffi::c_long, SyscallError> {
25 let legacy_syscall_fn =
30 |ctx: &mut SyscallContext| Self::legacy_syscall(cshadow::syscallhandler_fcntl, ctx);
31
32 let mut desc_table = ctx.objs.thread.descriptor_table_borrow_mut(ctx.objs.host);
34 let desc = Self::get_descriptor_mut(&mut desc_table, fd)?;
35
36 let Ok(cmd) = FcntlCommand::try_from(cmd) else {
37 debug!("Bad fcntl command: {cmd}");
38 return Err(Errno::EINVAL.into());
39 };
40
41 Ok(match cmd {
42 FcntlCommand::F_SETLK
43 | FcntlCommand::F_SETLKW
44 | FcntlCommand::F_OFD_SETLKW
45 | FcntlCommand::F_GETLK
46 | FcntlCommand::F_OFD_GETLK => {
47 match desc.file() {
48 CompatFile::New(_) => {
49 warn_once_then_debug!("fcntl({cmd:?}) unimplemented for {:?}", desc.file());
50 return Err(Errno::ENOSYS.into());
51 }
52 CompatFile::Legacy(_) => {
53 warn_once_then_debug!(
54 "Using fcntl({cmd:?}) implementation that assumes no lock contention. \
55 See https://github.com/shadow/shadow/issues/2258"
56 );
57 drop(desc_table);
58 return legacy_syscall_fn(ctx);
59 }
60 };
61 }
62 FcntlCommand::F_GETFL => {
63 let file = match desc.file() {
64 CompatFile::New(d) => d,
65 CompatFile::Legacy(_) => {
67 drop(desc_table);
68 return legacy_syscall_fn(ctx);
69 }
70 };
71
72 let file = file.inner_file().borrow();
73 let flags = file.status().as_o_flags() | file.mode().as_o_flags();
75 flags.bits().into()
76 }
77 FcntlCommand::F_SETFL => {
78 let file = match desc.file() {
79 CompatFile::New(d) => d,
80 CompatFile::Legacy(_) => {
82 drop(desc_table);
83 return legacy_syscall_fn(ctx);
84 }
85 };
86
87 let status = i32::try_from(arg).or(Err(Errno::EINVAL))?;
88 let mut status = OFlag::from_bits(status).ok_or(Errno::EINVAL)?;
89 status.remove(OFlag::O_RDONLY | OFlag::O_WRONLY | OFlag::O_RDWR | OFlag::O_PATH);
91 status.remove(
93 OFlag::O_CLOEXEC
94 | OFlag::O_CREAT
95 | OFlag::O_DIRECTORY
96 | OFlag::O_EXCL
97 | OFlag::O_NOCTTY
98 | OFlag::O_NOFOLLOW
99 | OFlag::O_TMPFILE
100 | OFlag::O_TRUNC,
101 );
102
103 let mut file = file.inner_file().borrow_mut();
104 let old_flags = file.status().as_o_flags();
105
106 let update_mask = OFlag::O_APPEND
109 | OFlag::O_ASYNC
110 | OFlag::O_DIRECT
111 | OFlag::O_NOATIME
112 | OFlag::O_NONBLOCK;
113
114 let status = (old_flags & !update_mask) | (status & update_mask);
129
130 let (status, remaining) = FileStatus::from_o_flags(status);
131
132 if !remaining.is_empty() {
134 return Err(Errno::EINVAL.into());
135 }
136
137 file.set_status(status);
138 0
139 }
140 FcntlCommand::F_GETFD => desc.flags().bits().into(),
141 FcntlCommand::F_SETFD => {
142 let flags = i32::try_from(arg).or(Err(Errno::EINVAL))?;
143 let flags = DescriptorFlags::from_bits(flags).ok_or(Errno::EINVAL)?;
144 desc.set_flags(flags);
145 0
146 }
147 FcntlCommand::F_DUPFD => {
148 let min_fd = arg.try_into().or(Err(Errno::EINVAL))?;
149
150 let new_desc = desc.dup(DescriptorFlags::empty());
151 let new_fd = desc_table
152 .register_descriptor_with_min_fd(new_desc, min_fd)
153 .or(Err(Errno::EINVAL))?;
154 new_fd.into()
155 }
156 FcntlCommand::F_DUPFD_CLOEXEC => {
157 let min_fd = arg.try_into().or(Err(Errno::EINVAL))?;
158
159 let new_desc = desc.dup(DescriptorFlags::FD_CLOEXEC);
160 let new_fd = desc_table
161 .register_descriptor_with_min_fd(new_desc, min_fd)
162 .or(Err(Errno::EINVAL))?;
163 new_fd.into()
164 }
165 FcntlCommand::F_GETPIPE_SZ => {
166 let file = match desc.file() {
167 CompatFile::New(d) => d,
168 CompatFile::Legacy(_) => {
170 return legacy_syscall_fn(ctx);
171 }
172 };
173
174 if let File::Pipe(pipe) = file.inner_file() {
175 pipe.borrow().max_size().try_into().unwrap()
176 } else {
177 return Err(Errno::EINVAL.into());
178 }
179 }
180 cmd => {
181 warn_once_then_debug!("Unhandled fcntl command: {cmd:?}");
182 return Err(Errno::EINVAL.into());
183 }
184 })
185 }
186}