Don't duplicate FDs if we are reasonably certain that it isn't needed.

This commit is contained in:
Mathieu Trossevin 2023-12-07 22:11:56 +01:00
parent 33aabb14d7
commit 4485c1f034

View file

@ -6,7 +6,7 @@ use std::os::unix::net::{UnixDatagram, UnixListener, UnixStream};
use std::{env, process};
use error::{GetFdsError, ReceiveError, ReceiveNameError};
use rustix::fd::{AsRawFd, BorrowedFd, OwnedFd};
use rustix::fd::{AsRawFd, BorrowedFd, OwnedFd, FromRawFd};
use rustix::fs::FileType;
use rustix::net::SocketType;
@ -18,8 +18,6 @@ const FD_NUMBER_VAR: &str = "LISTEN_FDS";
const FD_NAMES_VAR: &str = "LISTEN_FDNAMES";
/// File Descriptor passed by systemd.
///
/// They are duplicated from the actual passed file descriptor so as to be safe to use from rust code.
#[derive(Debug)]
pub enum FileDescriptor {
/// The file descriptor is a [`File`](std::fs::File).
@ -78,7 +76,10 @@ impl FileDescriptor {
/// Get any file descriptor passed by systemd or anything implementing the `LISTEN_FD` protocol.
///
/// This isn't necessarily limited to File descriptor of listening sockets, IPCs or FIFOs but also anything that is in the file descriptor store.
/// The file descriptores are duplicated using [`fcntl_dupfd_cloexec`](rustix::fs::fcntl_dupfd_cloexec) so they can safely be used from rust and will not be propagated to children process automatically.
///
/// If `unset_env` is `true` then the file descriptor are directly taken as if they were owned. This is only safe if this library is the only place taking these file descriptor but avoid unnecessary duplication of file descriptors.
///
/// If `unset_env` is `false` the file descriptores are duplicated using [`fcntl_dupfd_cloexec`](rustix::fs::fcntl_dupfd_cloexec) so they can safely be used from rust and will not be propagated to children process automatically.
///
/// # Errors
///
@ -113,7 +114,7 @@ impl FileDescriptor {
});
}
match Self::from_fds(fds) {
match Self::from_fds(fds, unset_env) {
Ok(fds) => Ok(fds),
Err(error) => Err(ReceiveError::GetFds(error)),
}
@ -148,25 +149,28 @@ impl FileDescriptor {
.zip(fd_names))
}
fn from_fds(num_fds: usize) -> Result<impl IntoIterator<Item = FileDescriptor>, GetFdsError> {
fn from_fds(num_fds: usize, unset_env: bool) -> Result<impl IntoIterator<Item = FileDescriptor>, GetFdsError> {
if SD_LISTEN_FDS_START.checked_add(num_fds as RawFd).is_none() {
return Err(GetFdsError::TooManyFDs(num_fds));
}
Ok((0..num_fds).map(|fd_offset| {
Ok((0..num_fds).map(move |fd_offset| {
SD_LISTEN_FDS_START
.checked_add(fd_offset as RawFd)
// SAFETY: We are receiving the fd so it should be safe
.map(|fd| FileDescriptor::from_fd(fd, 0))
.map(|fd| FileDescriptor::from_fd(fd, unset_env))
.expect("Already checked against overflow.")
}))
}
fn from_fd(fd: RawFd, min_new: RawFd) -> Self {
let fd = {
fn from_fd(fd: RawFd, unset_env: bool) -> Self {
let fd = if unset_env {
// SAFETY: The environement is removed so there shouldn't be anything new that might take it and close it.
unsafe { OwnedFd::from_raw_fd(fd) }
} else {
// SAFETY: The file descriptor won't be closed by the time we duplicate it.
let fd = unsafe { BorrowedFd::borrow_raw(fd) };
rustix::fs::fcntl_dupfd_cloexec(fd, min_new)
rustix::fs::fcntl_dupfd_cloexec(fd, 0)
.expect("Couldn't duplicate the file descriptor")
};
let stat = rustix::fs::fstat(&fd)