blob: 3cfb63f34228977d7afcda6d567c3357928b0677 [file] [log] [blame]
// Copyright 2020 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use std::convert::TryFrom;
use std::fs::File;
use std::io::{Stderr, Stdin, Stdout};
use std::mem;
use std::net::UdpSocket;
use std::ops::Drop;
use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
use std::os::unix::net::{UnixDatagram, UnixStream};
use serde::{Deserialize, Serialize};
use crate::net::UnlinkUnixSeqpacketListener;
use crate::{errno_result, PollToken, Result};
pub type RawDescriptor = RawFd;
/// Trait for forfeiting ownership of the current raw descriptor, and returning the raw descriptor
pub trait IntoRawDescriptor {
fn into_raw_descriptor(self) -> RawDescriptor;
}
/// Trait for returning the underlying raw descriptor, without giving up ownership of the
/// descriptor.
pub trait AsRawDescriptor {
fn as_raw_descriptor(&self) -> RawDescriptor;
}
pub trait FromRawDescriptor {
/// # Safety
/// Safe only if the caller ensures nothing has access to the descriptor after passing it to
/// `from_raw_descriptor`
unsafe fn from_raw_descriptor(descriptor: RawDescriptor) -> Self;
}
/// Clones `descriptor`, returning a new `RawDescriptor` that refers to the same open file
/// description as `descriptor`. The cloned descriptor will have the `FD_CLOEXEC` flag set but will
/// not share any other file descriptor flags with `descriptor`.
pub fn clone_descriptor(descriptor: &dyn AsRawDescriptor) -> Result<RawDescriptor> {
clone_fd(&descriptor.as_raw_descriptor())
}
/// Clones `fd`, returning a new file descriptor that refers to the same open file description as
/// `fd`. The cloned fd will have the `FD_CLOEXEC` flag set but will not share any other file
/// descriptor flags with `fd`.
pub fn clone_fd(fd: &dyn AsRawFd) -> Result<RawFd> {
// Safe because this doesn't modify any memory and we check the return value.
let ret = unsafe { libc::fcntl(fd.as_raw_fd(), libc::F_DUPFD_CLOEXEC, 0) };
if ret < 0 {
errno_result()
} else {
Ok(ret)
}
}
/// Wraps a RawDescriptor and safely closes it when self falls out of scope.
#[derive(Serialize, Deserialize, Debug, Eq)]
#[serde(transparent)]
pub struct SafeDescriptor {
#[serde(with = "crate::with_raw_descriptor")]
descriptor: RawDescriptor,
}
const KCMP_FILE: u32 = 0;
impl PartialEq for SafeDescriptor {
fn eq(&self, other: &Self) -> bool {
// If RawFd numbers match then we can return early without calling kcmp
if self.descriptor == other.descriptor {
return true;
}
// safe because we only use the return value and libc says it's always successful
let pid = unsafe { libc::getpid() };
// safe because we are passing everything by value and checking the return value
let ret = unsafe {
libc::syscall(
libc::SYS_kcmp,
pid,
pid,
KCMP_FILE,
self.descriptor,
other.descriptor,
)
};
ret == 0
}
}
impl Drop for SafeDescriptor {
fn drop(&mut self) {
let _ = unsafe { libc::close(self.descriptor) };
}
}
impl AsRawDescriptor for SafeDescriptor {
fn as_raw_descriptor(&self) -> RawDescriptor {
self.descriptor
}
}
impl IntoRawDescriptor for SafeDescriptor {
fn into_raw_descriptor(self) -> RawDescriptor {
let descriptor = self.descriptor;
mem::forget(self);
descriptor
}
}
impl FromRawDescriptor for SafeDescriptor {
unsafe fn from_raw_descriptor(descriptor: RawDescriptor) -> Self {
SafeDescriptor { descriptor }
}
}
impl AsRawFd for SafeDescriptor {
fn as_raw_fd(&self) -> RawFd {
self.as_raw_descriptor()
}
}
impl TryFrom<&dyn AsRawFd> for SafeDescriptor {
type Error = std::io::Error;
fn try_from(fd: &dyn AsRawFd) -> std::result::Result<Self, Self::Error> {
Ok(SafeDescriptor {
descriptor: clone_fd(fd)?,
})
}
}
impl SafeDescriptor {
/// Clones this descriptor, internally creating a new descriptor. The new SafeDescriptor will
/// share the same underlying count within the kernel.
pub fn try_clone(&self) -> Result<SafeDescriptor> {
// Safe because this doesn't modify any memory and we check the return value.
let descriptor = unsafe { libc::fcntl(self.descriptor, libc::F_DUPFD_CLOEXEC, 0) };
if descriptor < 0 {
errno_result()
} else {
Ok(SafeDescriptor { descriptor })
}
}
}
impl From<SafeDescriptor> for File {
fn from(s: SafeDescriptor) -> File {
// Safe because we own the SafeDescriptor at this point.
unsafe { File::from_raw_fd(s.into_raw_descriptor()) }
}
}
impl From<File> for SafeDescriptor {
fn from(f: File) -> SafeDescriptor {
// Safe because we own the File at this point.
unsafe { SafeDescriptor::from_raw_descriptor(f.into_raw_descriptor()) }
}
}
impl From<SafeDescriptor> for UnixStream {
fn from(s: SafeDescriptor) -> Self {
// Safe because we own the SafeDescriptor at this point.
unsafe { Self::from_raw_fd(s.into_raw_descriptor()) }
}
}
/// For use cases where a simple wrapper around a RawDescriptor is needed.
/// This is a simply a wrapper and does not manage the lifetime of the descriptor.
/// Most usages should prefer SafeDescriptor or using a RawDescriptor directly
#[derive(Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
#[repr(transparent)]
pub struct Descriptor(pub RawDescriptor);
impl AsRawDescriptor for Descriptor {
fn as_raw_descriptor(&self) -> RawDescriptor {
self.0
}
}
// AsRawFd for interoperability with interfaces that require it. Within crosvm,
// always use AsRawDescriptor when possible.
impl AsRawFd for Descriptor {
fn as_raw_fd(&self) -> RawFd {
self.0
}
}
/// Implement token for implementations that wish to use this struct as such
impl PollToken for Descriptor {
fn as_raw_token(&self) -> u64 {
self.0 as u64
}
fn from_raw_token(data: u64) -> Self {
Descriptor(data as RawDescriptor)
}
}
macro_rules! AsRawDescriptor {
($name:ident) => {
impl AsRawDescriptor for $name {
fn as_raw_descriptor(&self) -> RawDescriptor {
self.as_raw_fd()
}
}
};
}
macro_rules! FromRawDescriptor {
($name:ident) => {
impl FromRawDescriptor for $name {
unsafe fn from_raw_descriptor(descriptor: RawDescriptor) -> Self {
$name::from_raw_fd(descriptor)
}
}
};
}
macro_rules! IntoRawDescriptor {
($name:ident) => {
impl IntoRawDescriptor for $name {
fn into_raw_descriptor(self) -> RawDescriptor {
self.into_raw_fd()
}
}
};
}
// Implementations for File. This enables the File-type to use
// RawDescriptor, but does not mean File should be used as a generic
// descriptor container. That should go to either SafeDescriptor or another more
// relevant container type.
AsRawDescriptor!(File);
AsRawDescriptor!(UnlinkUnixSeqpacketListener);
AsRawDescriptor!(UdpSocket);
AsRawDescriptor!(UnixDatagram);
AsRawDescriptor!(UnixStream);
FromRawDescriptor!(File);
FromRawDescriptor!(UnixDatagram);
IntoRawDescriptor!(File);
IntoRawDescriptor!(UnixDatagram);
AsRawDescriptor!(Stdin);
AsRawDescriptor!(Stdout);
AsRawDescriptor!(Stderr);
#[test]
fn clone_equality() {
let ret = unsafe { libc::eventfd(0, 0) };
if ret < 0 {
panic!("failed to create eventfd");
}
let descriptor = unsafe { SafeDescriptor::from_raw_descriptor(ret) };
assert_eq!(descriptor, descriptor);
assert_eq!(
descriptor,
descriptor.try_clone().expect("failed to clone eventfd")
);
let ret = unsafe { libc::eventfd(0, 0) };
if ret < 0 {
panic!("failed to create eventfd");
}
let another = unsafe { SafeDescriptor::from_raw_descriptor(ret) };
assert_ne!(descriptor, another);
}