ensc_libc_util/
fd.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
#![allow(unused_unsafe)]

use std::os::{unix::io::RawFd, fd::{OwnedFd, AsRawFd, FromRawFd}};

#[derive(Debug)]
pub enum Fd {
    None,
    Owned(OwnedFd),
    Borrowed(RawFd),
}

fn _errno() -> i32 {
    std::io::Error::last_os_error().raw_os_error().unwrap_or(0)
}

fn retry<F: Fn() -> i32>(f: F) -> std::io::Result<i32> {
    loop {
	let rc = f();

	if rc >= 0 {
	    break Ok(rc);
	} else {
	    let e = std::io::Error::last_os_error();

	    if e.kind() != std::io::ErrorKind::Interrupted {
		break Err(e)
	    }
	}
    }
}

impl From<OwnedFd> for Fd {
    fn from(value: OwnedFd) -> Self {
        Self::Owned(value)
    }
}

impl Fd {
    /// # Safety
    ///
    /// The resource pointed to by `fd` must be open and suitable for assuming
    /// ownership. The resource must not require any cleanup other than `close`.
    pub unsafe fn from_raw_fd(fd: RawFd) -> Self {
	OwnedFd::from_raw_fd(fd).into()
    }

    /// # Safety
    ///
    /// Given fd must not be closed as long as the created Fd is alive.
    pub unsafe fn share_raw_fd(fd: RawFd) -> Self {
	Self::Borrowed(fd)
    }

    fn try_as_raw_fd(&self) -> Option<RawFd> {
	match self {
	    Self::None			=> None,
	    Self::Owned(fd)		=> Some(fd.as_raw_fd()),
	    Self::Borrowed(fd)		=> Some(fd.as_raw_fd()),
	}
    }

    pub fn close(&mut self) {
	*self = Self::None;
    }

    /// # Safety
    /// require p to match the request
    pub unsafe fn ioctl_ptr<T>(&self,  request: libc::c_ulong, p: *mut T) -> std::io::Result<i32> {
	match self.try_as_raw_fd() {
	    Some(fd)	=> retry(|| unsafe { libc::ioctl(fd, request, p) }),
	    None	=> Err(std::io::Error::from_raw_os_error(libc::EBADF)),
	}
    }

    /// # Safety
    /// require p to match the request
    pub unsafe fn ioctl_ptrc<T>(&self,  request: libc::c_ulong, p: *const T) -> std::io::Result<i32>
    {
	let p_mut: *mut T  = std::mem::transmute(p);

	self.ioctl_ptr(request, p_mut)
    }

    /// # Safety
    /// allocated memory must be released manually
    pub unsafe fn mmap(&self, addr: *mut libc::c_void, len: libc::size_t, prot: libc::c_int,
		       flags: libc::c_int, offset: libc::off_t) -> std::io::Result<*mut libc::c_void>
    {
	match self.try_as_raw_fd() {
	    Some(fd)	=> {
		match unsafe { libc::mmap(addr, len, prot, flags, fd, offset) } {
		    rc if rc == libc::MAP_FAILED	=> Err(std::io::Error::last_os_error()),
		    addr				=> Ok(addr),
		}
	    }

	    None	=> Err(std::io::Error::from_raw_os_error(libc::EBADF)),
	}
    }
}