dd: replace nix by rustix (#12567)

Co-authored-by: Sylvestre Ledru <sylvestre@debian.org>
This commit is contained in:
oech3
2026-06-04 17:03:24 +09:00
committed by GitHub
parent 22b4c6d15d
commit 64e536cbe6
5 changed files with 44 additions and 50 deletions
Generated
+1 -1
View File
@@ -3419,7 +3419,7 @@ dependencies = [
"fluent",
"gcd",
"libc",
"nix",
"rustix",
"tempfile",
"thiserror 2.0.18",
"uucore",
+1 -1
View File
@@ -33,7 +33,7 @@ thiserror = { workspace = true }
fluent = { workspace = true }
[target.'cfg(any(target_os = "linux", target_os = "android"))'.dependencies]
nix = { workspace = true, features = ["fs", "signal"] }
rustix = { workspace = true, features = ["fs"] }
[[bin]]
name = "dd"
+1 -1
View File
@@ -39,7 +39,7 @@ impl<'a> BufferedOutput<'a> {
}
}
pub(crate) fn discard_cache(&self, offset: libc::off_t, len: libc::off_t) {
pub(crate) fn discard_cache(&self, offset: u64, len: u64) {
self.inner.discard_cache(offset, len);
}
+38 -42
View File
@@ -3,7 +3,7 @@
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
// spell-checker:ignore fname, ftype, tname, fpath, specfile, testfile, unspec, ifile, ofile, outfile, fullblock, urand, fileio, atoe, atoibm, behaviour, bmax, bremain, cflags, creat, ctable, ctty, datastructures, doesnt, etoa, fileout, fname, gnudd, iconvflags, iseek, nocache, noctty, noerror, nofollow, nolinks, nonblock, oconvflags, oseek, outfile, parseargs, rlen, rmax, rremain, rsofar, rstat, sigusr, wlen, wstat oconv canonicalized Fadvise FADV DONTNEED ESPIPE bufferedoutput, SETFL
// spell-checker:ignore fname, ftype, tname, fpath, specfile, testfile, unspec, ifile, ofile, outfile, fullblock, urand, fileio, atoe, atoibm, behaviour, bmax, bremain, cflags, creat, ctable, ctty, datastructures, doesnt, etoa, fileout, fname, gnudd, iconvflags, iseek, nocache, noctty, noerror, nofollow, nolinks, nonblock, oconvflags, oseek, outfile, parseargs, rlen, rmax, rremain, rsofar, rstat, sigusr, wlen, wstat oconv canonicalized FADV DONTNEED ESPIPE SPIPE bufferedoutput, SETFL
mod blocks;
mod bufferedoutput;
@@ -16,10 +16,6 @@ mod progress;
use crate::bufferedoutput::BufferedOutput;
use blocks::conv_block_unblock_helper;
use datastructures::{ConversionMode, IConvFlags, IFlags, OConvFlags, OFlags, options};
#[cfg(any(target_os = "linux", target_os = "android"))]
use nix::fcntl::FcntlArg;
#[cfg(any(target_os = "linux", target_os = "android"))]
use nix::fcntl::OFlag;
use parseargs::Parser;
use progress::ProgUpdateType;
use progress::{ProgUpdate, ReadStat, StatusLevel, WriteStat, gen_prog_updater};
@@ -36,8 +32,6 @@ use std::fs::Metadata;
use std::fs::{File, OpenOptions};
use std::io::{self, Read, Seek, SeekFrom, Write};
#[cfg(any(target_os = "linux", target_os = "android"))]
use std::os::fd::AsFd;
#[cfg(any(target_os = "linux", target_os = "android"))]
use std::os::unix::fs::OpenOptionsExt;
#[cfg(unix)]
use std::os::unix::{
@@ -54,11 +48,6 @@ use std::time::{Duration, Instant};
use clap::{Arg, Command};
use gcd::Gcd;
#[cfg(target_os = "linux")]
use nix::{
errno::Errno,
fcntl::{PosixFadviseAdvice, posix_fadvise},
};
use uucore::display::Quotable;
use uucore::error::{FromIo, UResult};
#[cfg(unix)]
@@ -317,14 +306,16 @@ impl Source {
/// portion of the source is no longer needed. If not possible,
/// then this function returns an error.
#[cfg(target_os = "linux")]
fn discard_cache(&self, offset: libc::off_t, len: libc::off_t) -> nix::Result<()> {
fn discard_cache(&self, offset: u64, len: u64) -> io::Result<()> {
#[allow(clippy::match_wildcard_for_single_variants)]
match self {
Self::File(f) => {
let advice = PosixFadviseAdvice::POSIX_FADV_DONTNEED;
posix_fadvise(f.as_fd(), offset, len, advice)
use rustix::fs::{Advice::DontNeed, fadvise};
fadvise(f, offset, std::num::NonZeroU64::new(len), DontNeed)?;
Ok(())
}
_ => Err(Errno::ESPIPE), // "Illegal seek"
// fadvise for nonseekable returns this error. We manually do that...
_ => Err(rustix::io::Errno::SPIPE.into()),
}
}
}
@@ -502,7 +493,7 @@ impl Input<'_> {
/// function prints an error message to stderr and sets the exit
/// status code to 1.
#[cfg_attr(not(target_os = "linux"), allow(clippy::unused_self, unused_variables))]
fn discard_cache(&self, offset: libc::off_t, len: libc::off_t) {
fn discard_cache(&self, offset: u64, len: u64) {
#[cfg(target_os = "linux")]
{
let file = self
@@ -703,13 +694,15 @@ impl Dest {
/// specified portion of the destination is no longer needed. If
/// not possible, then this function returns an error.
#[cfg(target_os = "linux")]
fn discard_cache(&self, offset: libc::off_t, len: libc::off_t) -> nix::Result<()> {
fn discard_cache(&self, offset: u64, len: u64) -> io::Result<()> {
match self {
Self::File(f, _) => {
let advice = PosixFadviseAdvice::POSIX_FADV_DONTNEED;
posix_fadvise(f.as_fd(), offset, len, advice)
use rustix::fs::{Advice::DontNeed, fadvise};
fadvise(f, offset, std::num::NonZeroU64::new(len), DontNeed)?;
Ok(())
}
_ => Err(Errno::ESPIPE), // "Illegal seek"
// fadvise for nonseekable returns this error. We manually do that...
_ => Err(rustix::io::Errno::SPIPE.into()),
}
}
}
@@ -723,31 +716,33 @@ fn is_sparse(buf: &[u8]) -> bool {
/// This follows GNU dd behavior for partial block writes with O_DIRECT.
#[cfg(any(target_os = "linux", target_os = "android"))]
fn handle_o_direct_write(f: &mut File, buf: &[u8], original_error: io::Error) -> io::Result<usize> {
use nix::fcntl::{FcntlArg, OFlag, fcntl};
use rustix::fs::{OFlags, fcntl_getfl, fcntl_setfl};
// Get current flags using nix
let oflags = match fcntl(&mut *f, FcntlArg::F_GETFL) {
Ok(flags) => OFlag::from_bits_retain(flags),
Err(_) => return Err(original_error),
// Get current flags
let Ok(oflags) = fcntl_getfl(&*f) else {
return Err(original_error);
};
// If O_DIRECT is set, try removing it temporarily
if oflags.contains(OFlag::O_DIRECT) {
let flags_without_direct = oflags - OFlag::O_DIRECT;
if oflags.contains(OFlags::DIRECT) {
let flags_without_direct = oflags & !OFlags::DIRECT;
// Remove O_DIRECT flag using nix
if fcntl(&mut *f, FcntlArg::F_SETFL(flags_without_direct)).is_err() {
// Remove O_DIRECT flag
if fcntl_setfl(&*f, flags_without_direct).is_err() {
return Err(original_error);
}
// Retry the write without O_DIRECT
let write_result = f.write(buf);
// Restore O_DIRECT flag using nix (GNU doesn't restore it, but we'll be safer)
// Restore O_DIRECT flag (GNU doesn't restore it, but we'll be safer)
// Log any restoration errors without failing the operation
if let Err(os_err) = fcntl(&mut *f, FcntlArg::F_SETFL(oflags)) {
if let Err(os_err) = fcntl_setfl(&*f, oflags) {
// Just log the error, don't fail the whole operation
show_error!("Failed to restore O_DIRECT flag: {os_err}");
show_error!(
"Failed to restore O_DIRECT flag: {}",
io::Error::from(os_err)
);
}
write_result
@@ -894,10 +889,11 @@ impl<'a> Output<'a> {
let fx = OwnedFileDescriptorOrHandle::from(io::stdout())?;
#[cfg(any(target_os = "linux", target_os = "android"))]
if let Some(libc_flags) = make_linux_oflags(&settings.oflags) {
nix::fcntl::fcntl(
fx.as_raw().as_fd(),
FcntlArg::F_SETFL(OFlag::from_bits_retain(libc_flags)),
)?;
rustix::fs::fcntl_setfl(
fx.as_raw(),
rustix::fs::OFlags::from_bits_retain(libc_flags as _),
)
.map_err(|e| uucore::error::UIoError::from(io::Error::from(e)))?;
}
Self::prepare_file(fx.into_file(), settings)
@@ -940,7 +936,7 @@ impl<'a> Output<'a> {
/// this function prints an error message to stderr and sets the
/// exit status code to 1.
#[cfg_attr(not(target_os = "linux"), allow(clippy::unused_self, unused_variables))]
fn discard_cache(&self, offset: libc::off_t, len: libc::off_t) {
fn discard_cache(&self, offset: u64, len: u64) {
#[cfg(target_os = "linux")]
{
let file = self
@@ -1046,7 +1042,7 @@ enum BlockWriter<'a> {
}
impl BlockWriter<'_> {
fn discard_cache(&self, offset: libc::off_t, len: libc::off_t) {
fn discard_cache(&self, offset: u64, len: u64) {
match self {
Self::Unbuffered(o) => o.discard_cache(offset, len),
Self::Buffered(o) => o.discard_cache(offset, len),
@@ -1228,7 +1224,7 @@ fn dd_copy(mut i: Input, o: Output) -> io::Result<()> {
let rstat_update = read_helper(&mut i, &mut buf, loop_bsize)?;
if rstat_update.is_empty() {
if input_nocache {
i.discard_cache(read_offset.try_into().unwrap(), 0);
i.discard_cache(read_offset, 0);
}
if output_nocache || output_direct {
o.discard_cache(write_offset.try_into().unwrap(), 0);
@@ -1243,8 +1239,8 @@ fn dd_copy(mut i: Input, o: Output) -> io::Result<()> {
// TODO Better error handling for overflowing `offset` and `len`.
let read_len = rstat_update.bytes_total;
if input_nocache {
let offset = read_offset.try_into().unwrap();
let len = read_len.try_into().unwrap();
let offset = read_offset;
let len = read_len;
i.discard_cache(offset, len);
}
read_offset += read_len;
+3 -5
View File
@@ -459,11 +459,9 @@ extern "C" fn sigusr1_handler(_: std::os::raw::c_int) {
}
#[cfg(target_os = "linux")]
pub(crate) fn install_sigusr1_handler() -> Result<(), nix::errno::Errno> {
uucore::signals::install_signal_handler(
nix::sys::signal::Signal::SIGUSR1 as libc::c_int,
sigusr1_handler,
)
pub(crate) fn install_sigusr1_handler() -> std::io::Result<()> {
uucore::signals::install_signal_handler(libc::SIGUSR1, sigusr1_handler)?;
Ok(())
}
/// Return a closure that can be used in its own thread to print progress info.