owned_fd/
lib.rs

1//! Provide a general handler for file descriptor reasources via the `OwnedFd` and `FdRef` types
2
3use std::os::unix::io::{IntoRawFd,AsRawFd,FromRawFd,RawFd};
4use std::mem::{forget, transmute};
5use std::io;
6use std::borrow::{Borrow,ToOwned};
7use std::ops::{Deref};
8
9extern crate libc;
10
11unsafe fn dup(i: RawFd) -> io::Result<RawFd> {
12    let v = libc::dup(i);
13    if v < 0 {
14        Err(io::Error::last_os_error())
15    } else {
16        Ok(v)
17    }
18}
19
20/**
21 * OwnedFd is an RAII wrapper around RawFd: it automatically closes on drop & provides borrow()
22 * functionality to support lifetimes & borrow checking around the use of file descriptors
23 *
24 * Compared to using File, TcpStream, etc as a wrapper to close on drop, OwnedFd:
25 *
26 *  - allows any type of filedescriptor to be used safely
27 *  - has no overhead greater than a RawFd (no buffer, metadata, or other allocations)
28 *  - allows use of the borrow system to ensure drop (close) happens only when all users of an
29 *    ownedfd have released it.
30 */
31pub struct OwnedFd {
32    inner: RawFd,
33}
34
35impl OwnedFd {
36    /**
37     * Given a raw file descriptor that may be owned by another (ie: another data structure might
38     * close it), create a Owned version that we have control over (via dup())
39     *
40     * For taking ownership, see `FromRawFd::from_raw_fd()`.
41     *
42     * Unsafety:
43     *
44     *  - @i _must_ be a valid file descriptor (of any kind)
45     */
46    pub unsafe fn from_unowned_raw(i : RawFd) -> io::Result<OwnedFd> {
47        Ok(OwnedFd { inner: try!(dup(i)) })
48    }
49
50    /**
51     * Duplicate this OwnedFd, and allow the error to be detected.
52     *
53     * Clone uses this, but panics on error
54     */
55    pub fn dup(&self) -> io::Result<OwnedFd> {
56        unsafe { OwnedFd::from_unowned_raw(self.as_raw_fd()) }
57    }
58
59    /**
60     * Given a type that impliments `IntoRawFd` construct an OwnedFd.
61     *
62     * This is safe because we assume the promises of `IntoRawFd` are followed.
63     *
64     * NOTE: ideally, we'd impl this as From<T>, but current rust doesn't allow that. Revisit when
65     * specialization stabilizes.
66     */
67    pub fn from<T: IntoRawFd>(i: T) -> Self {
68        OwnedFd { inner: i.into_raw_fd() }
69    }
70}
71
72impl AsRawFd for OwnedFd {
73    fn as_raw_fd(&self) -> RawFd {
74        self.inner
75    }
76}
77
78
79impl IntoRawFd for OwnedFd {
80    fn into_raw_fd(self) -> RawFd {
81        let v = self.inner;
82        forget(self);
83        v
84    }
85}
86
87impl FromRawFd for OwnedFd {
88    unsafe fn from_raw_fd(fd: RawFd) -> OwnedFd {
89        OwnedFd { inner: fd }
90    }
91}
92
93impl Drop for OwnedFd {
94    fn drop(&mut self) {
95        unsafe { libc::close(self.inner) };
96    }
97}
98
99impl Clone for OwnedFd {
100    fn clone(&self) -> Self {
101        self.dup().unwrap()
102    }
103}
104
105/*
106 * WARNING: assumes RawFd and (*const _) are the same size! (or at least that RawFd is bounded by
107 * isize).
108 */
109impl Borrow<FdRef> for OwnedFd {
110    fn borrow(&self) -> &FdRef {
111        unsafe { FdRef::from_unowned_raw(self.as_raw_fd()) }
112    }
113}
114
115impl Deref for OwnedFd {
116    type Target = FdRef;
117    fn deref(&self) -> &Self::Target {
118        self.borrow()
119    }
120}
121
122/**
123 * A zero-cost (well, very, very, low cost) borrow of an OwnedFd.
124 *
125 * This cannot be constructed directly, and can only exist as `&FdRef`.
126 *
127 * As a result, it might be slightly larger than a bare RawFd.
128 */
129pub struct FdRef {
130    #[doc(hidden)]
131    __nothing: ()
132}
133
134impl FdRef {
135    /**
136     * Construct a FdRef reference from a RawFd. No ownership is taken.
137     *
138     * unsafety:
139     *
140     *  - @i _must_ be a valid fd
141     *  - the lifetime 'a must be appropriately bound
142     */
143    pub unsafe fn from_unowned_raw<'a>(i: RawFd) -> &'a FdRef{
144        transmute(i as isize)
145    }
146}
147
148impl AsRawFd for FdRef {
149    fn as_raw_fd(&self) -> RawFd {
150        let i : isize = unsafe { transmute(self) };
151        i as RawFd
152    }
153}
154
155impl ToOwned for FdRef {
156    type Owned = OwnedFd;
157    fn to_owned(&self) -> Self::Owned {
158        unsafe { OwnedFd::from_unowned_raw(self.as_raw_fd()).unwrap() }
159    }
160}
161
162#[cfg(test)]
163mod tests {
164    extern crate tempfile;
165    use super::{OwnedFd,FdRef};
166    use std::borrow::Borrow;
167    use std::os::unix::io::{AsRawFd};
168
169    #[test]
170    fn it_works() {
171        let t = tempfile::tempfile().unwrap();
172        let fd = OwnedFd::from(t);
173
174        let r : &FdRef = fd.borrow();
175
176        assert!(r.to_owned().as_raw_fd() != fd.as_raw_fd());
177    }
178}