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}