Skip to main content

relibc/header/sys_ioctl/redox/
mod.rs

1use core::{mem, ptr, slice};
2use redox_rt::proc::FdGuard;
3use syscall;
4
5use crate::{
6    error::{Errno, Result},
7    header::{bits_winsize::winsize, errno::EINVAL, fcntl, termios},
8    platform::{
9        Pal, Sys,
10        types::{c_int, c_ulong, c_ulonglong, c_void, pid_t},
11    },
12};
13
14use super::constants::*;
15
16mod drm;
17
18// TODO: some of the structs passed as T have padding bytes, so casting to a byte slice is UB
19
20fn dup_read<T>(fd: c_int, name: &str, t: &mut T) -> syscall::Result<usize> {
21    let dup = FdGuard::new(redox_rt::sys::dup(fd as usize, name.as_bytes())?);
22
23    let size = mem::size_of::<T>();
24
25    let bytes = dup.read(unsafe { slice::from_raw_parts_mut(t as *mut T as *mut u8, size) })?;
26
27    Ok(bytes / size)
28}
29
30// FIXME: unsound
31fn dup_write<T>(fd: c_int, name: &str, t: &T) -> Result<usize> {
32    let dup = FdGuard::new(redox_rt::sys::dup(fd as usize, name.as_bytes())?);
33
34    let size = mem::size_of::<T>();
35
36    let bytes = dup.write(unsafe { slice::from_raw_parts(t as *const T as *const u8, size) })?;
37
38    Ok(bytes / size)
39}
40
41#[derive(Debug)]
42enum IoctlBuffer {
43    None,
44    Read(*mut c_void, usize),    // read (write to userspace)
45    Write(*const c_void, usize), // write (read from userspace)
46    ReadWrite(*mut c_void, usize),
47}
48
49impl IoctlBuffer {
50    unsafe fn read<T>(&self) -> Result<T> {
51        let (ptr, size) = match *self {
52            Self::Write(ptr, size) => (ptr, size),
53            Self::ReadWrite(ptr, size) => (ptr as *const c_void, size),
54            _ => {
55                return Err(Errno(EINVAL));
56            }
57        };
58        if size == mem::size_of::<T>() {
59            let value = unsafe { ptr::read(ptr as *const T) };
60            Ok(value)
61        } else {
62            Err(Errno(EINVAL))
63        }
64    }
65
66    unsafe fn write<T>(&mut self, value: T) -> Result<()> {
67        let (ptr, size) = match *self {
68            Self::Read(ptr, size) | Self::ReadWrite(ptr, size) => (ptr, size),
69            _ => {
70                return Err(Errno(EINVAL));
71            }
72        };
73        if size == mem::size_of::<T>() {
74            unsafe { ptr::write(ptr as *mut T, value) };
75            Ok(())
76        } else {
77            Err(Errno(EINVAL))
78        }
79    }
80}
81
82pub unsafe fn ioctl_inner(fd: c_int, request: c_ulong, out: *mut c_void) -> Result<c_int> {
83    match request {
84        FIONBIO => {
85            let mut flags = Sys::fcntl(fd, fcntl::F_GETFL, 0)?;
86            flags = if unsafe { *(out as *mut c_int) } == 0 {
87                flags & !fcntl::O_NONBLOCK
88            } else {
89                flags | fcntl::O_NONBLOCK
90            };
91            Sys::fcntl(fd, fcntl::F_SETFL, flags as c_ulonglong)?;
92        }
93        TCGETS => {
94            let termios = unsafe { &mut *(out as *mut termios::termios) };
95            dup_read(fd, "termios", termios)?;
96        }
97        // TODO: give these different behaviors
98        TCSETS | TCSETSW | TCSETSF => {
99            let termios = unsafe { &*(out as *const termios::termios) };
100            dup_write(fd, "termios", termios)?;
101        }
102        TCFLSH => {
103            let queue = out as c_int;
104            dup_write(fd, "flush", &queue)?;
105        }
106        TIOCSCTTY => {
107            todo_skip!(0, "ioctl TIOCSCTTY");
108        }
109        TIOCGPGRP => {
110            let pgrp = unsafe { &mut *(out as *mut pid_t) };
111            dup_read(fd, "pgrp", pgrp)?;
112        }
113        TIOCSPGRP => {
114            let pgrp = unsafe { &*(out as *const pid_t) };
115            dup_write(fd, "pgrp", pgrp)?;
116        }
117        TIOCGWINSZ => {
118            let winsize = unsafe { &mut *(out as *mut winsize) };
119            dup_read(fd, "winsize", winsize)?;
120        }
121        TIOCSWINSZ => {
122            let winsize = unsafe { &*(out as *const winsize) };
123            dup_write(fd, "winsize", winsize)?;
124        }
125        TIOCGPTLCK => {
126            let lock = unsafe { &mut *(out as *mut c_int) };
127            dup_read(fd, "ptlock", lock)?;
128        }
129        TIOCSPTLCK => {
130            let lock = unsafe { &*(out as *const c_int) };
131            dup_write(fd, "ptlock", lock)?;
132        }
133        TIOCGPTN => {
134            let name = unsafe { &mut *(out as *mut c_int) };
135            dup_read(fd, "ptsname", name)?;
136        }
137        TCSBRK => {
138            todo_skip!(0, "ioctl TCSBRK");
139        }
140        TCXONC => {
141            todo_skip!(0, "ioctl TCXONC");
142        }
143        SIOCATMARK => {
144            todo_skip!(0, "ioctl SIOCATMARK");
145        }
146        _ => {
147            // See https://docs.kernel.org/userspace-api/ioctl/ioctl-decoding.html for details
148            let dir = (request >> 30) & 0b11;
149            let size = ((request >> 16) & 0x3FFF) as usize;
150            let name = (((request >> 8) & 0xFF) as u8) as char;
151            let func = (request & 0xFF) as u8;
152            match name {
153                'd' => {
154                    let buf = match dir {
155                        0b10 => IoctlBuffer::Read(out, size),
156                        0b01 => IoctlBuffer::Write(out, size),
157                        0b11 => IoctlBuffer::ReadWrite(out, size),
158                        _ => IoctlBuffer::None,
159                    };
160                    return unsafe { drm::ioctl(fd, func, buf) };
161                }
162                _ => {
163                    return Err(Errno(EINVAL));
164                }
165            }
166        }
167    }
168    Ok(0)
169}