xref: /aosp_15_r20/external/crosvm/base/src/sys/unix/descriptor.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
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 
5 use std::convert::TryFrom;
6 use std::fs::File;
7 use std::io::Stderr;
8 use std::io::Stdin;
9 use std::io::Stdout;
10 use std::net::TcpListener;
11 use std::net::TcpStream;
12 use std::net::UdpSocket;
13 use std::ops::Drop;
14 use std::os::fd::OwnedFd;
15 use std::os::unix::io::AsRawFd;
16 use std::os::unix::io::FromRawFd;
17 use std::os::unix::io::IntoRawFd;
18 use std::os::unix::io::RawFd;
19 use std::os::unix::net::UnixDatagram;
20 use std::os::unix::net::UnixListener;
21 use std::os::unix::net::UnixStream;
22 
23 use crate::descriptor::AsRawDescriptor;
24 use crate::descriptor::Descriptor;
25 use crate::descriptor::FromRawDescriptor;
26 use crate::descriptor::IntoRawDescriptor;
27 use crate::descriptor::SafeDescriptor;
28 use crate::errno::errno_result;
29 use crate::errno::Result;
30 
31 pub type RawDescriptor = RawFd;
32 
33 pub 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`.
clone_descriptor(descriptor: &(impl AsRawDescriptor + ?Sized)) -> Result<SafeDescriptor>38 pub 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`.
clone_fd(fd: RawFd) -> Result<SafeDescriptor>45 fn 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
set_descriptor_cloexec<A: AsRawDescriptor>(fd_owner: &A) -> Result<()>58 pub 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
clear_descriptor_cloexec<A: AsRawDescriptor>(fd_owner: &A) -> Result<()>65 pub 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.
modify_descriptor_flags( desc: RawDescriptor, modify_flags: impl FnOnce(libc::c_int) -> libc::c_int, ) -> Result<()>72 fn 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 
94 impl Drop for SafeDescriptor {
drop(&mut self)95     fn drop(&mut self) {
96         // SAFETY:
97         // Safe because descriptor is valid.
98         let _ = unsafe { libc::close(self.descriptor) };
99     }
100 }
101 
102 impl AsRawFd for SafeDescriptor {
as_raw_fd(&self) -> RawFd103     fn as_raw_fd(&self) -> RawFd {
104         self.as_raw_descriptor()
105     }
106 }
107 
108 impl TryFrom<&dyn AsRawFd> for SafeDescriptor {
109     type Error = std::io::Error;
110 
try_from(fd: &dyn AsRawFd) -> std::result::Result<Self, Self::Error>111     fn try_from(fd: &dyn AsRawFd) -> std::result::Result<Self, Self::Error> {
112         Ok(clone_fd(fd.as_raw_fd())?)
113     }
114 }
115 
116 impl SafeDescriptor {
117     /// Clones this descriptor, internally creating a new descriptor. The new SafeDescriptor will
118     /// share the same underlying count within the kernel.
try_clone(&self) -> Result<SafeDescriptor>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 
131 impl From<SafeDescriptor> for File {
from(s: SafeDescriptor) -> File132     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 
139 impl From<SafeDescriptor> for TcpListener {
from(s: SafeDescriptor) -> Self140     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 
147 impl From<SafeDescriptor> for TcpStream {
from(s: SafeDescriptor) -> Self148     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 
155 impl From<SafeDescriptor> for UnixStream {
from(s: SafeDescriptor) -> Self156     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 
163 impl From<SafeDescriptor> for OwnedFd {
from(s: SafeDescriptor) -> Self164     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 
171 impl From<OwnedFd> for SafeDescriptor {
from(fd: OwnedFd) -> Self172     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.
181 impl AsRawFd for Descriptor {
as_raw_fd(&self) -> RawFd182     fn as_raw_fd(&self) -> RawFd {
183         self.0
184     }
185 }
186 
187 macro_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 
197 macro_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 
207 macro_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.
221 AsRawDescriptor!(File);
222 AsRawDescriptor!(OwnedFd);
223 AsRawDescriptor!(TcpListener);
224 AsRawDescriptor!(TcpStream);
225 AsRawDescriptor!(UdpSocket);
226 AsRawDescriptor!(UnixDatagram);
227 AsRawDescriptor!(UnixListener);
228 AsRawDescriptor!(UnixStream);
229 FromRawDescriptor!(File);
230 FromRawDescriptor!(OwnedFd);
231 FromRawDescriptor!(UnixStream);
232 FromRawDescriptor!(UnixDatagram);
233 IntoRawDescriptor!(File);
234 IntoRawDescriptor!(OwnedFd);
235 IntoRawDescriptor!(UnixDatagram);
236 IntoRawDescriptor!(UnixStream);
237 AsRawDescriptor!(Stdin);
238 AsRawDescriptor!(Stdout);
239 AsRawDescriptor!(Stderr);
240