Skip to content

Commit 3c6079f

Browse files
committed
re-use existing fd for stdout even if its a seek-able file
this is important as the fd holds the file offset we need to use
1 parent f8e5296 commit 3c6079f

File tree

2 files changed

+38
-6
lines changed

2 files changed

+38
-6
lines changed

src/uu/dd/Cargo.toml

+1-3
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,9 @@ gcd = { workspace = true }
2020
libc = { workspace = true }
2121
uucore = { workspace = true, features = ["format", "quoting-style"] }
2222

23-
[target.'cfg(any(target_os = "linux"))'.dependencies]
24-
nix = { workspace = true, features = ["fs"] }
25-
2623
[target.'cfg(any(target_os = "linux", target_os = "android"))'.dependencies]
2724
signal-hook = { workspace = true }
25+
nix = { workspace = true, features = ["fs"] }
2826

2927
[[bin]]
3028
name = "dd"

src/uu/dd/src/dd.rs

+37-3
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
// For the full copyright and license information, please view the LICENSE
44
// file that was distributed with this source code.
55

6-
// 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
6+
// 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
77

88
mod blocks;
99
mod bufferedoutput;
@@ -16,6 +16,10 @@ mod progress;
1616
use crate::bufferedoutput::BufferedOutput;
1717
use blocks::conv_block_unblock_helper;
1818
use datastructures::*;
19+
#[cfg(any(target_os = "linux", target_os = "android"))]
20+
use nix::fcntl::FcntlArg::F_SETFL;
21+
#[cfg(any(target_os = "linux", target_os = "android"))]
22+
use nix::fcntl::OFlag;
1923
use parseargs::Parser;
2024
use progress::{gen_prog_updater, ProgUpdate, ReadStat, StatusLevel, WriteStat};
2125

@@ -24,6 +28,8 @@ use std::env;
2428
use std::ffi::OsString;
2529
use std::fs::{File, OpenOptions};
2630
use std::io::{self, Read, Seek, SeekFrom, Stdout, Write};
31+
use std::os::fd::AsFd;
32+
use std::os::fd::OwnedFd;
2733
#[cfg(any(target_os = "linux", target_os = "android"))]
2834
use std::os::unix::fs::OpenOptionsExt;
2935
#[cfg(unix)]
@@ -557,7 +563,7 @@ impl Dest {
557563
return Ok(len);
558564
}
559565
}
560-
f.seek(io::SeekFrom::Start(n))
566+
f.seek(io::SeekFrom::Current(n.try_into().unwrap()))
561567
}
562568
#[cfg(unix)]
563569
Self::Fifo(f) => {
@@ -699,6 +705,11 @@ impl<'a> Output<'a> {
699705
if !settings.oconv.notrunc {
700706
dst.set_len(settings.seek).ok();
701707
}
708+
709+
Self::prepare_file(dst, settings)
710+
}
711+
712+
fn prepare_file(dst: File, settings: &'a Settings) -> UResult<Self> {
702713
let density = if settings.oconv.sparse {
703714
Density::Sparse
704715
} else {
@@ -710,6 +721,28 @@ impl<'a> Output<'a> {
710721
Ok(Self { dst, settings })
711722
}
712723

724+
/// Instantiate this struct with file descriptor as a destination.
725+
///
726+
/// This is useful e.g. for the case when the file descriptor was
727+
/// already opened by the system (stdout) and has a state
728+
/// (current position) that shall be used.
729+
fn new_file_from_fd(fd: OwnedFd, settings: &'a Settings) -> UResult<Self> {
730+
fn open_dst(fd: OwnedFd, _oflags: &OFlags) -> Result<File, io::Error> {
731+
#[cfg(any(target_os = "linux", target_os = "android"))]
732+
if let Some(libc_flags) = make_linux_oflags(_oflags) {
733+
nix::fcntl::fcntl(fd.as_raw_fd(), F_SETFL(OFlag::from_bits_retain(libc_flags)))?;
734+
}
735+
736+
Ok(File::from(fd))
737+
}
738+
739+
let raw_fd = fd.as_raw_fd();
740+
let dst = open_dst(fd, &settings.oflags)
741+
.map_err_context(|| format!("failed to open fd {}", raw_fd))?;
742+
743+
Self::prepare_file(dst, settings)
744+
}
745+
713746
/// Instantiate this struct with the given named pipe as a destination.
714747
#[cfg(unix)]
715748
fn new_fifo(filename: &Path, settings: &'a Settings) -> UResult<Self> {
@@ -1288,7 +1321,8 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
12881321
Some(ref outfile) if is_fifo(outfile) => Output::new_fifo(Path::new(&outfile), &settings)?,
12891322
Some(ref outfile) => Output::new_file(Path::new(&outfile), &settings)?,
12901323
None if is_stdout_redirected_to_seekable_file() => {
1291-
Output::new_file(Path::new(&stdout_canonicalized()), &settings)?
1324+
let fd = io::stdout().as_fd().try_clone_to_owned()?;
1325+
Output::new_file_from_fd(fd, &settings)?
12921326
}
12931327
None => Output::new_stdout(&settings)?,
12941328
};

0 commit comments

Comments
 (0)