os/kernel/syscall/
ioctl.rs1use crate::arch::trap::SumGuard;
6use crate::kernel::current_task;
7use crate::uapi::errno::{EBADF, EINVAL, ENOTTY, EOPNOTSUPP};
8use crate::uapi::ioctl::*;
9use crate::vfs::FsError;
10use crate::{earlyprintln, pr_debug, pr_err, pr_warn};
11
12pub fn ioctl(fd: i32, request: u32, arg: usize) -> isize {
46 pr_debug!(
47 "ioctl: fd={}, request={:#x} ({}), arg={:#x}",
48 fd,
49 request,
50 request,
51 arg
52 );
53
54 if fd < 0 {
56 pr_err!("ioctl: invalid fd {}", fd);
57 return -EBADF as isize;
58 }
59
60 let task = current_task();
62 let file = {
63 let task_lock = task.lock();
64 match task_lock.fd_table.get(fd as usize) {
65 Ok(f) => f,
66 Err(_) => {
67 pr_err!("ioctl: fd {} not found", fd);
68 return -EBADF as isize;
69 }
70 }
71 };
72
73 let result = match request {
75 FIONBIO => handle_fionbio(&file, arg),
77 FIONREAD => handle_fionread(&file, arg),
78 FIOASYNC => handle_fioasync(&file, arg),
79
80 TIOCGWINSZ | TIOCSWINSZ | TCGETS | TCSETS | TCSETSW | TCSETSF => {
82 match file.ioctl(request, arg) {
83 Ok(ret) => ret,
84 Err(FsError::NotSupported) => {
85 pr_warn!(
86 "ioctl: fd={}, terminal request {:#x} ({}) not supported by file type",
87 fd,
88 request,
89 request
90 );
91 -ENOTTY as isize
92 }
93 Err(e) => fs_error_to_errno(e),
94 }
95 }
96
97 TIOCGPGRP => handle_tiocgpgrp(&task, arg),
99 TIOCSPGRP => handle_tiocspgrp(&task, arg),
100
101 TIOCSCTTY => handle_tiocsctty(&file, arg),
103
104 VT_OPENQRY => handle_vt_openqry(arg),
106
107 SIOCGIFCONF => handle_siocgifconf(arg),
109 SIOCGIFADDR | SIOCSIFADDR | SIOCGIFFLAGS | SIOCSIFFLAGS | SIOCGIFNETMASK
110 | SIOCSIFNETMASK | SIOCGIFMTU | SIOCSIFMTU | SIOCGIFHWADDR | SIOCSIFHWADDR
111 | SIOCGIFINDEX => handle_ifreq(&file, request, arg),
112
113 _ => {
116 pr_debug!("ioctl: delegating request {:#x} to file object", request);
117 match file.ioctl(request, arg) {
118 Ok(ret) => ret,
119 Err(FsError::NotSupported) => {
120 pr_warn!(
121 "ioctl: unsupported request {:#x} (type={:#x}, nr={}, size={})",
122 request,
123 _IOC_TYPE(request),
124 _IOC_NR(request),
125 _IOC_SIZE(request)
126 );
127 -ENOTTY as isize
128 }
129 Err(e) => {
130 pr_err!("ioctl: file ioctl failed: {:?}", e);
131 fs_error_to_errno(e)
132 }
133 }
134 }
135 };
136
137 pr_debug!(
138 "ioctl: fd={}, request={:#x} => result={}",
139 fd,
140 request,
141 result
142 );
143 result
144}
145
146fn handle_fionbio(file: &alloc::sync::Arc<dyn crate::vfs::File>, arg: usize) -> isize {
150 unsafe {
151 let _guard = SumGuard::new();
152 let value_ptr = arg as *const i32;
153 if value_ptr.is_null() {
154 return -EINVAL as isize;
155 }
156
157 let value = core::ptr::read_volatile(value_ptr);
158
159 let mut flags = file.flags();
161 if value != 0 {
162 flags |= crate::uapi::fcntl::OpenFlags::O_NONBLOCK;
163 } else {
164 flags &= !crate::uapi::fcntl::OpenFlags::O_NONBLOCK;
165 }
166
167 match file.set_status_flags(flags) {
168 Ok(_) => 0,
169 Err(e) => {
170 pr_warn!("ioctl: FIONBIO failed: {:?}", e);
171 -EOPNOTSUPP as isize
172 }
173 }
174 }
175}
176
177fn handle_fionread(file: &alloc::sync::Arc<dyn crate::vfs::File>, arg: usize) -> isize {
179 unsafe {
180 let _guard = SumGuard::new();
181 let value_ptr = arg as *mut i32;
182 if value_ptr.is_null() {
183 return -EINVAL as isize;
184 }
185
186 let available = match file.metadata() {
188 Ok(meta) => {
189 let size = meta.size;
190 let offset = file.offset();
191 if size > offset {
192 (size - offset) as i32
193 } else {
194 0
195 }
196 }
197 Err(_) => {
198 0
200 }
201 };
202
203 core::ptr::write_volatile(value_ptr, available);
204 0
205 }
206}
207
208fn handle_fioasync(file: &alloc::sync::Arc<dyn crate::vfs::File>, arg: usize) -> isize {
210 unsafe {
211 let _guard = SumGuard::new();
212 let value_ptr = arg as *const i32;
213 if value_ptr.is_null() {
214 return -EINVAL as isize;
215 }
216
217 let _value = core::ptr::read_volatile(value_ptr);
218
219 pr_warn!("ioctl: FIOASYNC not yet implemented");
221 -EOPNOTSUPP as isize
222 }
223}
224
225fn handle_tiocgpgrp(
229 task: &alloc::sync::Arc<crate::sync::SpinLock<crate::kernel::task::TaskStruct>>,
230 arg: usize,
231) -> isize {
232 if arg == 0 {
233 return -EINVAL as isize;
234 }
235
236 let pgid = task.lock().pgid as i32;
237 earlyprintln!("ioctl: TIOCGPGRP writing pgid={} to {:#x}", pgid, arg);
238
239 unsafe {
240 crate::util::user_buffer::write_to_user(arg as *mut i32, pgid);
241 }
242
243 earlyprintln!("ioctl: TIOCGPGRP completed");
244 0
245}
246
247fn handle_tiocspgrp(
249 task: &alloc::sync::Arc<crate::sync::SpinLock<crate::kernel::task::TaskStruct>>,
250 arg: usize,
251) -> isize {
252 unsafe {
253 let _guard = SumGuard::new();
254 let pid_ptr = arg as *const i32;
255 if pid_ptr.is_null() {
256 return -EINVAL as isize;
257 }
258
259 let pgid = core::ptr::read_volatile(pid_ptr);
260
261 task.lock().pgid = pgid as u32;
263
264 earlyprintln!("ioctl: TIOCSPGRP set pgid={}", pgid);
265 0
266 }
267}
268
269fn handle_tiocsctty(_file: &alloc::sync::Arc<dyn crate::vfs::File>, _arg: usize) -> isize {
275 earlyprintln!("ioctl: TIOCSCTTY accepted (not fully implemented)");
278 0
279}
280
281fn handle_vt_openqry(arg: usize) -> isize {
286 if arg == 0 {
287 return -EINVAL as isize;
288 }
289
290 earlyprintln!("ioctl: VT_OPENQRY not supported (no VT subsystem)");
292 -ENOTTY as isize
293}
294
295fn handle_siocgifconf(arg: usize) -> isize {
299 unsafe {
300 let _guard = SumGuard::new();
301 let ifconf_ptr = arg as *mut Ifconf;
302 if ifconf_ptr.is_null() {
303 return -EINVAL as isize;
304 }
305
306 let ifconf = core::ptr::read_volatile(ifconf_ptr);
308
309 core::ptr::write_bytes(ifconf_ptr, 0, 1);
311
312 let mut new_ifconf = ifconf;
315 new_ifconf.ifc_len = 0;
316 core::ptr::write_volatile(ifconf_ptr, new_ifconf);
317
318 earlyprintln!("ioctl: SIOCGIFCONF returned 0 interfaces");
319 0
320 }
321}
322
323fn handle_ifreq(_file: &alloc::sync::Arc<dyn crate::vfs::File>, request: u32, arg: usize) -> isize {
325 unsafe {
326 let _guard = SumGuard::new();
327 let ifreq_ptr = arg as *mut Ifreq;
328 if ifreq_ptr.is_null() {
329 return -EINVAL as isize;
330 }
331
332 let _ifreq = core::ptr::read_volatile(ifreq_ptr);
333
334 match request {
336 SIOCGIFADDR | SIOCGIFFLAGS | SIOCGIFNETMASK | SIOCGIFMTU | SIOCGIFHWADDR
337 | SIOCGIFINDEX => {
338 earlyprintln!("ioctl: network get request {:#x} not implemented", request);
339 -EOPNOTSUPP as isize
340 }
341 SIOCSIFADDR | SIOCSIFFLAGS | SIOCSIFNETMASK | SIOCSIFMTU | SIOCSIFHWADDR => {
342 earlyprintln!("ioctl: network set request {:#x} not implemented", request);
343 -EOPNOTSUPP as isize
344 }
345 _ => -EINVAL as isize,
346 }
347 }
348}
349
350fn fs_error_to_errno(err: FsError) -> isize {
354 match err {
355 FsError::NotSupported => -EOPNOTSUPP as isize,
356 FsError::InvalidArgument => -EINVAL as isize,
357 FsError::NotFound => -crate::uapi::errno::ENOENT as isize,
358 FsError::PermissionDenied => -crate::uapi::errno::EACCES as isize,
359 FsError::AlreadyExists => -crate::uapi::errno::EEXIST as isize,
360 FsError::IoError => -crate::uapi::errno::EIO as isize,
361 _ => -crate::uapi::errno::EIO as isize,
362 }
363}