Skip to content

Commit

Permalink
re-use existing fd for stdout even if its a seek-able file
Browse files Browse the repository at this point in the history
this is important as the fd holds the file offset we need to use
  • Loading branch information
cre4ture committed Mar 16, 2024
1 parent f8e5296 commit 3c6079f
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 6 deletions.
4 changes: 1 addition & 3 deletions src/uu/dd/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,9 @@ gcd = { workspace = true }
libc = { workspace = true }
uucore = { workspace = true, features = ["format", "quoting-style"] }

[target.'cfg(any(target_os = "linux"))'.dependencies]
nix = { workspace = true, features = ["fs"] }

[target.'cfg(any(target_os = "linux", target_os = "android"))'.dependencies]
signal-hook = { workspace = true }
nix = { workspace = true, features = ["fs"] }

[[bin]]
name = "dd"
Expand Down
40 changes: 37 additions & 3 deletions src/uu/dd/src/dd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 seekable oconv canonicalized fadvise Fadvise FADV DONTNEED ESPIPE bufferedoutput
// 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 seekable oconv canonicalized fadvise Fadvise FADV DONTNEED ESPIPE bufferedoutput, SETFL

mod blocks;
mod bufferedoutput;
Expand All @@ -16,6 +16,10 @@ mod progress;
use crate::bufferedoutput::BufferedOutput;
use blocks::conv_block_unblock_helper;
use datastructures::*;
#[cfg(any(target_os = "linux", target_os = "android"))]
use nix::fcntl::FcntlArg::F_SETFL;
#[cfg(any(target_os = "linux", target_os = "android"))]
use nix::fcntl::OFlag;
use parseargs::Parser;
use progress::{gen_prog_updater, ProgUpdate, ReadStat, StatusLevel, WriteStat};

Expand All @@ -24,6 +28,8 @@ use std::env;
use std::ffi::OsString;
use std::fs::{File, OpenOptions};
use std::io::{self, Read, Seek, SeekFrom, Stdout, Write};
use std::os::fd::AsFd;
use std::os::fd::OwnedFd;
#[cfg(any(target_os = "linux", target_os = "android"))]
use std::os::unix::fs::OpenOptionsExt;
#[cfg(unix)]
Expand Down Expand Up @@ -557,7 +563,7 @@ impl Dest {
return Ok(len);
}
}
f.seek(io::SeekFrom::Start(n))
f.seek(io::SeekFrom::Current(n.try_into().unwrap()))
}
#[cfg(unix)]
Self::Fifo(f) => {
Expand Down Expand Up @@ -699,6 +705,11 @@ impl<'a> Output<'a> {
if !settings.oconv.notrunc {
dst.set_len(settings.seek).ok();
}

Self::prepare_file(dst, settings)
}

fn prepare_file(dst: File, settings: &'a Settings) -> UResult<Self> {
let density = if settings.oconv.sparse {
Density::Sparse
} else {
Expand All @@ -710,6 +721,28 @@ impl<'a> Output<'a> {
Ok(Self { dst, settings })
}

/// Instantiate this struct with file descriptor as a destination.
///
/// This is useful e.g. for the case when the file descriptor was
/// already opened by the system (stdout) and has a state
/// (current position) that shall be used.
fn new_file_from_fd(fd: OwnedFd, settings: &'a Settings) -> UResult<Self> {
fn open_dst(fd: OwnedFd, _oflags: &OFlags) -> Result<File, io::Error> {
#[cfg(any(target_os = "linux", target_os = "android"))]
if let Some(libc_flags) = make_linux_oflags(_oflags) {
nix::fcntl::fcntl(fd.as_raw_fd(), F_SETFL(OFlag::from_bits_retain(libc_flags)))?;
}

Ok(File::from(fd))
}

let raw_fd = fd.as_raw_fd();
let dst = open_dst(fd, &settings.oflags)
.map_err_context(|| format!("failed to open fd {}", raw_fd))?;

Self::prepare_file(dst, settings)
}

/// Instantiate this struct with the given named pipe as a destination.
#[cfg(unix)]
fn new_fifo(filename: &Path, settings: &'a Settings) -> UResult<Self> {
Expand Down Expand Up @@ -1288,7 +1321,8 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
Some(ref outfile) if is_fifo(outfile) => Output::new_fifo(Path::new(&outfile), &settings)?,
Some(ref outfile) => Output::new_file(Path::new(&outfile), &settings)?,
None if is_stdout_redirected_to_seekable_file() => {
Output::new_file(Path::new(&stdout_canonicalized()), &settings)?
let fd = io::stdout().as_fd().try_clone_to_owned()?;
Output::new_file_from_fd(fd, &settings)?
}
None => Output::new_stdout(&settings)?,
};
Expand Down

0 comments on commit 3c6079f

Please sign in to comment.