Skip to content

Commit

Permalink
dd: add discard_cache() funcs for Input, Output
Browse files Browse the repository at this point in the history
Add the `Input::discard_cache()` and `Output::discard_cache()`
functions. These allow discarding the filesystem cache when `dd` no
longer needs to access a specified portion of the input or output
file, respectively.
  • Loading branch information
jfinkels committed Mar 12, 2023
1 parent a9af8d6 commit f36b6d1
Show file tree
Hide file tree
Showing 3 changed files with 112 additions and 1 deletion.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions src/uu/dd/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ path = "src/dd.rs"
clap = { workspace=true }
gcd = { workspace=true }
libc = { workspace=true }
nix = { workspace=true, features = ["fs"] }
uucore = { workspace=true }

[target.'cfg(any(target_os = "linux", target_os = "android"))'.dependencies]
Expand Down
111 changes: 110 additions & 1 deletion src/uu/dd/src/dd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.

// spell-checker:ignore fname, 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
// spell-checker:ignore fname, 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

mod datastructures;
use datastructures::*;
Expand Down Expand Up @@ -39,6 +39,11 @@ use std::time;

use clap::{crate_version, Arg, Command};
use gcd::Gcd;
#[cfg(all(unix, not(macos)))]
use nix::{
errno::Errno,
fcntl::{posix_fadvise, PosixFadviseAdvice},
};
use uucore::display::Quotable;
use uucore::error::{FromIo, UResult};
use uucore::{format_usage, help_about, help_section, help_usage, show_error};
Expand Down Expand Up @@ -106,6 +111,16 @@ enum Source {
}

impl Source {
/// The length of the data source in number of bytes.
///
/// If it cannot be determined, then this function returns 0.
fn len(&self) -> std::io::Result<i64> {
match self {
Self::File(f) => Ok(f.metadata()?.len().try_into().unwrap_or(i64::MAX)),
_ => Ok(0),
}
}

fn skip(&mut self, n: u64) -> io::Result<u64> {
match self {
Self::Stdin(stdin) => match io::copy(&mut stdin.take(n), &mut io::sink()) {
Expand All @@ -121,6 +136,23 @@ impl Source {
Self::Fifo(f) => io::copy(&mut f.take(n), &mut io::sink()),
}
}

/// Discard the system file cache for the given portion of the data source.
///
/// `offset` and `len` specify a contiguous portion of the data
/// source. This function informs the kernel that the specified
/// portion of the source is no longer needed. If not possible,
/// then this function returns an error.
#[cfg(all(unix, not(macos)))]
fn discard_cache(&self, offset: i64, len: i64) -> nix::Result<()> {
match self {
Self::File(f) => {
let advice = PosixFadviseAdvice::POSIX_FADV_DONTNEED;
posix_fadvise(f.as_raw_fd(), offset, len, advice)
}
_ => Err(Errno::ESPIPE), // "Illegal seek"
}
}
}

impl Read for Source {
Expand Down Expand Up @@ -255,6 +287,31 @@ impl<'a> Read for Input<'a> {
}

impl<'a> Input<'a> {
/// Discard the system file cache for the given portion of the input.
///
/// `offset` and `len` specify a contiguous portion of the input.
/// This function informs the kernel that the specified portion of
/// the input file is no longer needed. If not possible, then this
/// function prints an error message to stderr and sets the exit
/// status code to 1.
fn discard_cache(&self, offset: i64, len: i64) {
#[cfg(all(unix, not(macos)))]
{
if let Err(e) = self.src.discard_cache(offset, len) {
show_error!(
"failed to discard cache for: 'standard input': {}",
e.desc()
);
set_exit_code(1);
}
}
#[cfg(any(not(unix), macos))]
{
// TODO Is there a way to discard filesystem cache on
// these other operating systems?
}
}

/// Fills a given buffer.
/// Reads in increments of 'self.ibs'.
/// The start of each ibs-sized read follows the previous one.
Expand Down Expand Up @@ -410,6 +467,33 @@ impl Dest {
_ => Ok(()),
}
}

/// Discard the system file cache for the given portion of the destination.
///
/// `offset` and `len` specify a contiguous portion of the
/// destination. This function informs the kernel that the
/// specified portion of the destination is no longer needed. If
/// not possible, then this function returns an error.
#[cfg(all(unix, not(macos)))]
fn discard_cache(&self, offset: i64, len: i64) -> nix::Result<()> {
match self {
Self::File(f, _) => {
let advice = PosixFadviseAdvice::POSIX_FADV_DONTNEED;
posix_fadvise(f.as_raw_fd(), offset, len, advice)
}
_ => Err(Errno::ESPIPE), // "Illegal seek"
}
}

/// The length of the data destination in number of bytes.
///
/// If it cannot be determined, then this function returns 0.
fn len(&self) -> std::io::Result<i64> {
match self {
Self::File(f, _) => Ok(f.metadata()?.len().try_into().unwrap_or(i64::MAX)),
_ => Ok(0),
}
}
}

/// Decide whether the given buffer is all zeros.
Expand Down Expand Up @@ -543,6 +627,31 @@ impl<'a> Output<'a> {
Ok(Self { dst, settings })
}

/// Discard the system file cache for the given portion of the output.
///
/// `offset` and `len` specify a contiguous portion of the output.
/// This function informs the kernel that the specified portion of
/// the output file is no longer needed. If not possible, then
/// this function prints an error message to stderr and sets the
/// exit status code to 1.
fn discard_cache(&self, offset: i64, len: i64) {
#[cfg(all(unix, not(macos)))]
{
if let Err(e) = self.dst.discard_cache(offset, len) {
show_error!(
"failed to discard cache for: 'standard output': {}",
e.desc()
);
set_exit_code(1);
}
}
#[cfg(any(not(unix), macos))]
{
// TODO Is there a way to discard filesystem cache on
// these other operating systems?
}
}

/// Write the given bytes one block at a time.
///
/// This may write partial blocks (for example, if the underlying
Expand Down

0 comments on commit f36b6d1

Please sign in to comment.