base/sys/unix/
descriptor.rs

1// Copyright 2020 The ChromiumOS Authors
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use std::convert::TryFrom;
6use std::fs::File;
7use std::io::Stderr;
8use std::io::Stdin;
9use std::io::Stdout;
10use std::net::TcpListener;
11use std::net::TcpStream;
12use std::net::UdpSocket;
13use std::ops::Drop;
14use std::os::fd::OwnedFd;
15use std::os::unix::io::AsRawFd;
16use std::os::unix::io::FromRawFd;
17use std::os::unix::io::IntoRawFd;
18use std::os::unix::io::RawFd;
19use std::os::unix::net::UnixDatagram;
20use std::os::unix::net::UnixListener;
21use std::os::unix::net::UnixStream;
22
23use crate::descriptor::AsRawDescriptor;
24use crate::descriptor::Descriptor;
25use crate::descriptor::FromRawDescriptor;
26use crate::descriptor::IntoRawDescriptor;
27use crate::descriptor::SafeDescriptor;
28use crate::errno::errno_result;
29use crate::errno::Result;
30
31pub type RawDescriptor = RawFd;
32
33pub const INVALID_DESCRIPTOR: RawDescriptor = -1;
34
35/// Clones `descriptor`, returning a new `SafeDescriptor` that refers to the same file
36/// `descriptor`. The cloned descriptor will have the `FD_CLOEXEC` flag set but will not share any
37/// other file descriptor flags with `descriptor`.
38pub fn clone_descriptor(descriptor: &(impl AsRawDescriptor + ?Sized)) -> Result<SafeDescriptor> {
39    clone_fd(descriptor.as_raw_descriptor())
40}
41
42/// Clones `fd`, returning a new file descriptor that refers to the same open file as `fd`. The
43/// cloned fd will have the `FD_CLOEXEC` flag set but will not share any other file descriptor
44/// flags with `fd`.
45fn clone_fd(fd: RawFd) -> Result<SafeDescriptor> {
46    // SAFETY:
47    // Safe because this doesn't modify any memory and we check the return value.
48    let ret = unsafe { libc::fcntl(fd, libc::F_DUPFD_CLOEXEC, 0) };
49    if ret < 0 {
50        errno_result()
51    } else {
52        // SAFETY: We just dup'd the FD and so have exclusive access.
53        Ok(unsafe { SafeDescriptor::from_raw_descriptor(ret) })
54    }
55}
56
57/// Adds CLOEXEC flag on descriptor
58pub fn set_descriptor_cloexec<A: AsRawDescriptor>(fd_owner: &A) -> Result<()> {
59    modify_descriptor_flags(fd_owner.as_raw_descriptor(), |flags| {
60        flags | libc::FD_CLOEXEC
61    })
62}
63
64/// Clears CLOEXEC flag on descriptor
65pub fn clear_descriptor_cloexec<A: AsRawDescriptor>(fd_owner: &A) -> Result<()> {
66    modify_descriptor_flags(fd_owner.as_raw_descriptor(), |flags| {
67        flags & !libc::FD_CLOEXEC
68    })
69}
70
71/// Apply the specified modification to the file descriptor's flags.
72fn modify_descriptor_flags(
73    desc: RawDescriptor,
74    modify_flags: impl FnOnce(libc::c_int) -> libc::c_int,
75) -> Result<()> {
76    // SAFETY:
77    // Safe because fd is read only.
78    let flags = unsafe { libc::fcntl(desc, libc::F_GETFD) };
79    if flags == -1 {
80        return errno_result();
81    }
82
83    let new_flags = modify_flags(flags);
84
85    // SAFETY:
86    // Safe because this has no side effect(s) on the current process.
87    if new_flags != flags && unsafe { libc::fcntl(desc, libc::F_SETFD, new_flags) } == -1 {
88        errno_result()
89    } else {
90        Ok(())
91    }
92}
93
94impl Drop for SafeDescriptor {
95    fn drop(&mut self) {
96        // SAFETY:
97        // Safe because descriptor is valid.
98        let _ = unsafe { libc::close(self.descriptor) };
99    }
100}
101
102impl AsRawFd for SafeDescriptor {
103    fn as_raw_fd(&self) -> RawFd {
104        self.as_raw_descriptor()
105    }
106}
107
108impl TryFrom<&dyn AsRawFd> for SafeDescriptor {
109    type Error = std::io::Error;
110
111    fn try_from(fd: &dyn AsRawFd) -> std::result::Result<Self, Self::Error> {
112        Ok(clone_fd(fd.as_raw_fd())?)
113    }
114}
115
116impl SafeDescriptor {
117    /// Clones this descriptor, internally creating a new descriptor. The new SafeDescriptor will
118    /// share the same underlying count within the kernel.
119    pub fn try_clone(&self) -> Result<SafeDescriptor> {
120        // SAFETY:
121        // Safe because this doesn't modify any memory and we check the return value.
122        let descriptor = unsafe { libc::fcntl(self.descriptor, libc::F_DUPFD_CLOEXEC, 0) };
123        if descriptor < 0 {
124            errno_result()
125        } else {
126            Ok(SafeDescriptor { descriptor })
127        }
128    }
129}
130
131impl From<SafeDescriptor> for File {
132    fn from(s: SafeDescriptor) -> File {
133        // SAFETY:
134        // Safe because we own the SafeDescriptor at this point.
135        unsafe { File::from_raw_fd(s.into_raw_descriptor()) }
136    }
137}
138
139impl From<SafeDescriptor> for TcpListener {
140    fn from(s: SafeDescriptor) -> Self {
141        // SAFETY:
142        // Safe because we own the SafeDescriptor at this point.
143        unsafe { Self::from_raw_fd(s.into_raw_descriptor()) }
144    }
145}
146
147impl From<SafeDescriptor> for TcpStream {
148    fn from(s: SafeDescriptor) -> Self {
149        // SAFETY:
150        // Safe because we own the SafeDescriptor at this point.
151        unsafe { Self::from_raw_fd(s.into_raw_descriptor()) }
152    }
153}
154
155impl From<SafeDescriptor> for UnixStream {
156    fn from(s: SafeDescriptor) -> Self {
157        // SAFETY:
158        // Safe because we own the SafeDescriptor at this point.
159        unsafe { Self::from_raw_fd(s.into_raw_descriptor()) }
160    }
161}
162
163impl From<SafeDescriptor> for OwnedFd {
164    fn from(s: SafeDescriptor) -> Self {
165        // SAFETY:
166        // Safe because we own the SafeDescriptor at this point.
167        unsafe { OwnedFd::from_raw_descriptor(s.into_raw_descriptor()) }
168    }
169}
170
171impl From<OwnedFd> for SafeDescriptor {
172    fn from(fd: OwnedFd) -> Self {
173        // SAFETY:
174        // Safe because we own the OwnedFd at this point.
175        unsafe { SafeDescriptor::from_raw_descriptor(fd.into_raw_descriptor()) }
176    }
177}
178
179// AsRawFd for interoperability with interfaces that require it. Within crosvm,
180// always use AsRawDescriptor when possible.
181impl AsRawFd for Descriptor {
182    fn as_raw_fd(&self) -> RawFd {
183        self.0
184    }
185}
186
187macro_rules! AsRawDescriptor {
188    ($name:ident) => {
189        impl AsRawDescriptor for $name {
190            fn as_raw_descriptor(&self) -> RawDescriptor {
191                self.as_raw_fd()
192            }
193        }
194    };
195}
196
197macro_rules! FromRawDescriptor {
198    ($name:ident) => {
199        impl FromRawDescriptor for $name {
200            unsafe fn from_raw_descriptor(descriptor: RawDescriptor) -> Self {
201                $name::from_raw_fd(descriptor)
202            }
203        }
204    };
205}
206
207macro_rules! IntoRawDescriptor {
208    ($name:ident) => {
209        impl IntoRawDescriptor for $name {
210            fn into_raw_descriptor(self) -> RawDescriptor {
211                self.into_raw_fd()
212            }
213        }
214    };
215}
216
217// Implementations for File. This enables the File-type to use
218// RawDescriptor, but does not mean File should be used as a generic
219// descriptor container. That should go to either SafeDescriptor or another more
220// relevant container type.
221AsRawDescriptor!(File);
222AsRawDescriptor!(OwnedFd);
223AsRawDescriptor!(TcpListener);
224AsRawDescriptor!(TcpStream);
225AsRawDescriptor!(UdpSocket);
226AsRawDescriptor!(UnixDatagram);
227AsRawDescriptor!(UnixListener);
228AsRawDescriptor!(UnixStream);
229FromRawDescriptor!(File);
230FromRawDescriptor!(OwnedFd);
231FromRawDescriptor!(UnixStream);
232FromRawDescriptor!(UnixDatagram);
233IntoRawDescriptor!(File);
234IntoRawDescriptor!(OwnedFd);
235IntoRawDescriptor!(UnixDatagram);
236IntoRawDescriptor!(UnixStream);
237AsRawDescriptor!(Stdin);
238AsRawDescriptor!(Stdout);
239AsRawDescriptor!(Stderr);