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