blob: 4c86ca20598bd9e378d012e7de66a697fccb5948 [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::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 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;
}
/// Wraps a RawDescriptor and safely closes it when self falls out of scope.
#[derive(Debug, Eq)]
pub struct SafeDescriptor {
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 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 self.as_raw_descriptor() returns a valid value
let copy_fd = unsafe { libc::dup(self.as_raw_descriptor()) };
if copy_fd < 0 {
return errno_result();
}
// Safe becuase we just successfully duplicated and this object will uniquely
// own the raw descriptor.
Ok(unsafe { SafeDescriptor::from_raw_descriptor(copy_fd) })
}
}
impl Into<File> for SafeDescriptor {
fn into(self) -> File {
// Safe because we own the SafeDescriptor at this point.
unsafe { File::from_raw_fd(self.into_raw_descriptor()) }
}
}
impl Into<SafeDescriptor> for File {
fn into(self) -> SafeDescriptor {
// Safe because we own the File at this point.
unsafe { SafeDescriptor::from_raw_descriptor(self.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);
FromRawDescriptor!(File);
IntoRawDescriptor!(File);
AsRawDescriptor!(Stdin);
AsRawDescriptor!(Stdout);
AsRawDescriptor!(Stderr);
impl AsRawDescriptor for UdpSocket {
fn as_raw_descriptor(&self) -> RawDescriptor {
self.as_raw_fd()
}
}
impl AsRawDescriptor for UnixStream {
fn as_raw_descriptor(&self) -> RawDescriptor {
self.as_raw_fd()
}
}
impl AsRawDescriptor for UnixDatagram {
fn as_raw_descriptor(&self) -> RawDescriptor {
self.as_raw_fd()
}
}
impl FromRawDescriptor for UnixDatagram {
unsafe fn from_raw_descriptor(descriptor: RawDescriptor) -> Self {
Self::from_raw_fd(descriptor)
}
}
impl IntoRawDescriptor for UnixDatagram {
fn into_raw_descriptor(self) -> RawDescriptor {
self.into_raw_fd()
}
}
#[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);
}