Skip to content

Port cargo's libc usage to rustix. #10309

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ ignore = "0.4.7"
lazy_static = "1.2.0"
jobserver = "0.1.24"
lazycell = "1.2.0"
libc = "0.2"
rustix = "0.32.1"
log = "0.4.6"
libgit2-sys = "0.12.24"
memchr = "2.1.3"
Expand Down
3 changes: 2 additions & 1 deletion crates/cargo-util/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ crypto-hash = "0.3.1"
filetime = "0.2.9"
hex = "0.4.2"
jobserver = "0.1.21"
libc = "0.2.88"
rustix = "0.32.1"
io-lifetimes = "0.4.4"
log = "0.4.6"
same-file = "1.0.6"
shell-escape = "0.1.4"
Expand Down
31 changes: 16 additions & 15 deletions crates/cargo-util/src/process_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,25 +104,26 @@ pub fn exit_status_to_string(status: ExitStatus) -> String {

#[cfg(unix)]
fn status_to_string(status: ExitStatus) -> String {
use rustix::process::Signal;
use std::os::unix::process::*;

if let Some(signal) = status.signal() {
let name = match signal as libc::c_int {
libc::SIGABRT => ", SIGABRT: process abort signal",
libc::SIGALRM => ", SIGALRM: alarm clock",
libc::SIGFPE => ", SIGFPE: erroneous arithmetic operation",
libc::SIGHUP => ", SIGHUP: hangup",
libc::SIGILL => ", SIGILL: illegal instruction",
libc::SIGINT => ", SIGINT: terminal interrupt signal",
libc::SIGKILL => ", SIGKILL: kill",
libc::SIGPIPE => ", SIGPIPE: write on a pipe with no one to read",
libc::SIGQUIT => ", SIGQUIT: terminal quit signal",
libc::SIGSEGV => ", SIGSEGV: invalid memory reference",
libc::SIGTERM => ", SIGTERM: termination signal",
libc::SIGBUS => ", SIGBUS: access to undefined memory",
let name = match Signal::from_raw(signal) {
Some(Signal::Abort) => ", SIGABRT: process abort signal",
Some(Signal::Alarm) => ", SIGALRM: alarm clock",
Some(Signal::Fpe) => ", SIGFPE: erroneous arithmetic operation",
Some(Signal::Hup) => ", SIGHUP: hangup",
Some(Signal::Ill) => ", SIGILL: illegal instruction",
Some(Signal::Int) => ", SIGINT: terminal interrupt signal",
Some(Signal::Kill) => ", SIGKILL: kill",
Some(Signal::Pipe) => ", SIGPIPE: write on a pipe with no one to read",
Some(Signal::Quit) => ", SIGQUIT: terminal quit signal",
Some(Signal::Segv) => ", SIGSEGV: invalid memory reference",
Some(Signal::Term) => ", SIGTERM: termination signal",
Some(Signal::Bus) => ", SIGBUS: access to undefined memory",
#[cfg(not(target_os = "haiku"))]
libc::SIGSYS => ", SIGSYS: bad system call",
libc::SIGTRAP => ", SIGTRAP: trace/breakpoint trap",
Some(Signal::Sys) => ", SIGSYS: bad system call",
Some(Signal::Trap) => ", SIGTRAP: trace/breakpoint trap",
_ => "",
};
format!("signal: {}{}", signal, name)
Expand Down
50 changes: 24 additions & 26 deletions crates/cargo-util/src/read2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,52 +2,50 @@ pub use self::imp::read2;

#[cfg(unix)]
mod imp {
use io_lifetimes::AsFilelike;
use rustix::io::{PollFd, PollFlags};
use std::fs::File;
use std::io;
use std::io::prelude::*;
use std::mem;
use std::os::unix::prelude::*;
use std::process::{ChildStderr, ChildStdout};

pub fn read2(
mut out_pipe: ChildStdout,
mut err_pipe: ChildStderr,
out_pipe: ChildStdout,
err_pipe: ChildStderr,
data: &mut dyn FnMut(bool, &mut Vec<u8>, bool),
) -> io::Result<()> {
unsafe {
libc::fcntl(out_pipe.as_raw_fd(), libc::F_SETFL, libc::O_NONBLOCK);
libc::fcntl(err_pipe.as_raw_fd(), libc::F_SETFL, libc::O_NONBLOCK);
}
rustix::fs::fcntl_setfl(&out_pipe, rustix::fs::OFlags::NONBLOCK)?;
rustix::fs::fcntl_setfl(&err_pipe, rustix::fs::OFlags::NONBLOCK)?;

let mut out_done = false;
let mut err_done = false;
let mut out = Vec::new();
let mut err = Vec::new();

let mut fds: [libc::pollfd; 2] = unsafe { mem::zeroed() };
fds[0].fd = out_pipe.as_raw_fd();
fds[0].events = libc::POLLIN;
fds[1].fd = err_pipe.as_raw_fd();
fds[1].events = libc::POLLIN;
let mut fds = [
PollFd::new(&out_pipe, PollFlags::IN),
PollFd::new(&err_pipe, PollFlags::IN),
];
let mut nfds = 2;
let mut errfd = 1;

while nfds > 0 {
// wait for either pipe to become readable using `select`
let r = unsafe { libc::poll(fds.as_mut_ptr(), nfds, -1) };
if r == -1 {
let err = io::Error::last_os_error();
if err.kind() == io::ErrorKind::Interrupted {
continue;
}
return Err(err);
}
// wait for either pipe to become readable using `poll`
match rustix::io::poll(&mut fds[..nfds], -1) {
Ok(_num) => (),
Err(rustix::io::Error::INTR) => continue,
Err(err) => return Err(err.into()),
};

// Read as much as we can from each pipe, ignoring EWOULDBLOCK or
// EAGAIN. If we hit EOF, then this will happen because the underlying
// reader will return Ok(0), in which case we'll see `Ok` ourselves. In
// this case we flip the other fd back into blocking mode and read
// whatever's leftover on that file descriptor.
let handle = |res: io::Result<_>| match res {
let handle = |poll_fd: &PollFd, buf: &mut Vec<u8>| match poll_fd
.as_filelike_view::<File>()
.read_to_end(buf)
{
Ok(_) => Ok(true),
Err(e) => {
if e.kind() == io::ErrorKind::WouldBlock {
Expand All @@ -57,14 +55,14 @@ mod imp {
}
}
};
if !err_done && fds[errfd].revents != 0 && handle(err_pipe.read_to_end(&mut err))? {
if !err_done && !fds[errfd].revents().is_empty() && handle(&fds[errfd], &mut err)? {
err_done = true;
nfds -= 1;
}
data(false, &mut err, err_done);
if !out_done && fds[0].revents != 0 && handle(out_pipe.read_to_end(&mut out))? {
if !out_done && !fds[0].revents().is_empty() && handle(&fds[0], &mut out)? {
out_done = true;
fds[0].fd = err_pipe.as_raw_fd();
fds[0].set_fd(&err_pipe);
errfd = 0;
nfds -= 1;
}
Expand Down
8 changes: 1 addition & 7 deletions src/cargo/core/shell.rs
Original file line number Diff line number Diff line change
Expand Up @@ -460,16 +460,10 @@ impl ColorChoice {
#[cfg(unix)]
mod imp {
use super::{Shell, TtyWidth};
use std::mem;

pub fn stderr_width() -> TtyWidth {
unsafe {
let mut winsize: libc::winsize = mem::zeroed();
// The .into() here is needed for FreeBSD which defines TIOCGWINSZ
// as c_uint but ioctl wants c_ulong.
if libc::ioctl(libc::STDERR_FILENO, libc::TIOCGWINSZ.into(), &mut winsize) < 0 {
return TtyWidth::NoTty;
}
let winsize = rustix::io::ioctl_tiocgwinsz(&rustix::io::stderr()).unwrap();
if winsize.ws_col > 0 {
TtyWidth::Known(winsize.ws_col as usize)
} else {
Expand Down
2 changes: 1 addition & 1 deletion src/cargo/util/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1604,7 +1604,7 @@ impl Config {
}

#[cfg(unix)]
return io.raw_os_error() == Some(libc::EROFS);
return io.raw_os_error() == Some(rustix::io::Error::ROFS.raw_os_error());
}

false
Expand Down
50 changes: 22 additions & 28 deletions src/cargo/util/flock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -278,8 +278,8 @@ fn acquire(
config: &Config,
msg: &str,
path: &Path,
lock_try: &dyn Fn() -> io::Result<()>,
lock_block: &dyn Fn() -> io::Result<()>,
lock_try: &dyn Fn() -> std::io::Result<()>,
lock_block: &dyn Fn() -> std::io::Result<()>,
) -> CargoResult<()> {
// File locking on Unix is currently implemented via `flock`, which is known
// to be broken on NFS. We could in theory just ignore errors that happen on
Expand Down Expand Up @@ -317,85 +317,79 @@ fn acquire(
lock_block().with_context(|| format!("failed to lock file: {}", path.display()))?;
return Ok(());

#[cfg(all(target_os = "linux", not(target_env = "musl")))]
#[cfg(target_os = "linux")]
fn is_on_nfs_mount(path: &Path) -> bool {
use std::ffi::CString;
use std::mem;
use std::os::unix::prelude::*;

let path = match CString::new(path.as_os_str().as_bytes()) {
Ok(path) => path,
Err(_) => return false,
};

unsafe {
let mut buf: libc::statfs = mem::zeroed();
let r = libc::statfs(path.as_ptr(), &mut buf);

r == 0 && buf.f_type as u32 == libc::NFS_SUPER_MAGIC as u32
if let Ok(buf) = rustix::fs::statfs(path) {
buf.f_type as u32 == rustix::fs::NFS_SUPER_MAGIC as u32
} else {
false
}
}

#[cfg(any(not(target_os = "linux"), target_env = "musl"))]
#[cfg(not(target_os = "linux"))]
fn is_on_nfs_mount(_path: &Path) -> bool {
false
}
}

#[cfg(unix)]
mod sys {
use rustix::fs::FlockOperation;
use std::fs::File;
use std::io::{Error, Result};
use std::os::unix::io::AsRawFd;

pub(super) fn lock_shared(file: &File) -> Result<()> {
flock(file, libc::LOCK_SH)
flock(file, FlockOperation::LockShared)
}

pub(super) fn lock_exclusive(file: &File) -> Result<()> {
flock(file, libc::LOCK_EX)
flock(file, FlockOperation::LockExclusive)
}

pub(super) fn try_lock_shared(file: &File) -> Result<()> {
flock(file, libc::LOCK_SH | libc::LOCK_NB)
flock(file, FlockOperation::NonBlockingLockShared)
}

pub(super) fn try_lock_exclusive(file: &File) -> Result<()> {
flock(file, libc::LOCK_EX | libc::LOCK_NB)
flock(file, FlockOperation::NonBlockingLockExclusive)
}

pub(super) fn unlock(file: &File) -> Result<()> {
flock(file, libc::LOCK_UN)
flock(file, FlockOperation::Unlock)
}

pub(super) fn error_contended(err: &Error) -> bool {
err.raw_os_error().map_or(false, |x| x == libc::EWOULDBLOCK)
rustix::io::Error::from_io_error(err).map_or(false, |x| x == rustix::io::Error::WOULDBLOCK)
}

pub(super) fn error_unsupported(err: &Error) -> bool {
match err.raw_os_error() {
match rustix::io::Error::from_io_error(err) {
// Unfortunately, depending on the target, these may or may not be the same.
// For targets in which they are the same, the duplicate pattern causes a warning.
#[allow(unreachable_patterns)]
Some(libc::ENOTSUP | libc::EOPNOTSUPP) => true,
Some(rustix::io::Error::NOTSUP) | Some(rustix::io::Error::OPNOTSUPP) => true,
#[cfg(target_os = "linux")]
Some(libc::ENOSYS) => true,
Some(rustix::io::Error::NOSYS) => true,
_ => false,
}
}

#[cfg(not(target_os = "solaris"))]
fn flock(file: &File, flag: libc::c_int) -> Result<()> {
let ret = unsafe { libc::flock(file.as_raw_fd(), flag) };
if ret < 0 {
Err(Error::last_os_error())
} else {
Ok(())
}
fn flock(file: &File, flag: FlockOperation) -> Result<()> {
rustix::fs::flock(file, flag)?;
Ok(())
}

#[cfg(target_os = "solaris")]
fn flock(file: &File, flag: libc::c_int) -> Result<()> {
fn flock(file: &File, flag: FlockOperation) -> Result<()> {
// Solaris lacks flock(), so simply succeed with a no-op
Ok(())
}
Expand Down
2 changes: 1 addition & 1 deletion src/cargo/util/job.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ mod imp {
// one cargo spawned to become its own session leader, so we do that
// here.
if env::var("__CARGO_TEST_SETSID_PLEASE_DONT_USE_ELSEWHERE").is_ok() {
libc::setsid();
rustix::process::setsid().unwrap();
}
Some(())
}
Expand Down
9 changes: 5 additions & 4 deletions tests/testsuite/death.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,10 +89,11 @@ fn ctrl_c_kills_everyone() {

#[cfg(unix)]
pub fn ctrl_c(child: &mut Child) {
let r = unsafe { libc::kill(-(child.id() as i32), libc::SIGINT) };
if r < 0 {
panic!("failed to kill: {}", io::Error::last_os_error());
}
rustix::process::kill_process_group(
rustix::process::Pid::from_child(child),
rustix::process::Signal::Int,
)
.expect("failed to kill");
}

#[cfg(windows)]
Expand Down