uu_dd/
dd.rs

1// This file is part of the uutils coreutils package.
2//
3// For the full copyright and license information, please view the LICENSE
4// file that was distributed with this source code.
5
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
7
8mod blocks;
9mod bufferedoutput;
10mod conversion_tables;
11mod datastructures;
12mod numbers;
13mod parseargs;
14mod progress;
15
16use crate::bufferedoutput::BufferedOutput;
17use blocks::conv_block_unblock_helper;
18use datastructures::*;
19#[cfg(any(target_os = "linux", target_os = "android"))]
20use nix::fcntl::FcntlArg::F_SETFL;
21#[cfg(any(target_os = "linux", target_os = "android"))]
22use nix::fcntl::OFlag;
23use parseargs::Parser;
24use progress::ProgUpdateType;
25use progress::{ProgUpdate, ReadStat, StatusLevel, WriteStat, gen_prog_updater};
26use uucore::io::OwnedFileDescriptorOrHandle;
27use uucore::translate;
28
29use std::cmp;
30use std::env;
31use std::ffi::OsString;
32use std::fs::{File, OpenOptions};
33use std::io::{self, Read, Seek, SeekFrom, Stdout, Write};
34#[cfg(any(target_os = "linux", target_os = "android"))]
35use std::os::fd::AsFd;
36#[cfg(any(target_os = "linux", target_os = "android"))]
37use std::os::unix::fs::OpenOptionsExt;
38#[cfg(unix)]
39use std::os::unix::{
40    fs::FileTypeExt,
41    io::{AsRawFd, FromRawFd},
42};
43#[cfg(windows)]
44use std::os::windows::{fs::MetadataExt, io::AsHandle};
45use std::path::Path;
46use std::sync::atomic::AtomicU8;
47use std::sync::{Arc, atomic::Ordering::Relaxed, mpsc};
48use std::thread;
49use std::time::{Duration, Instant};
50
51use clap::{Arg, Command};
52use gcd::Gcd;
53#[cfg(target_os = "linux")]
54use nix::{
55    errno::Errno,
56    fcntl::{PosixFadviseAdvice, posix_fadvise},
57};
58use uucore::display::Quotable;
59use uucore::error::{FromIo, UResult};
60#[cfg(unix)]
61use uucore::error::{USimpleError, set_exit_code};
62#[cfg(target_os = "linux")]
63use uucore::show_if_err;
64use uucore::{format_usage, show_error};
65
66const BUF_INIT_BYTE: u8 = 0xDD;
67
68/// Final settings after parsing
69#[derive(Default)]
70struct Settings {
71    infile: Option<String>,
72    outfile: Option<String>,
73    ibs: usize,
74    obs: usize,
75    skip: u64,
76    seek: u64,
77    count: Option<Num>,
78    iconv: IConvFlags,
79    iflags: IFlags,
80    oconv: OConvFlags,
81    oflags: OFlags,
82    status: Option<StatusLevel>,
83    /// Whether the output writer should buffer partial blocks until complete.
84    buffered: bool,
85}
86
87/// A timer which triggers on a given interval
88///
89/// After being constructed with [`Alarm::with_interval`], [`Alarm::get_trigger`]
90/// will return [`ALARM_TRIGGER_TIMER`] once per the given [`Duration`].
91/// Alarm can be manually triggered with closure returned by [`Alarm::manual_trigger_fn`].
92/// [`Alarm::get_trigger`] will return [`ALARM_TRIGGER_SIGNAL`] in this case.
93///
94/// Can be cloned, but the trigger status is shared across all instances so only
95/// the first caller each interval will yield true.
96///
97/// When all instances are dropped the background thread will exit on the next interval.
98pub struct Alarm {
99    interval: Duration,
100    trigger: Arc<AtomicU8>,
101}
102
103pub const ALARM_TRIGGER_NONE: u8 = 0;
104pub const ALARM_TRIGGER_TIMER: u8 = 1;
105pub const ALARM_TRIGGER_SIGNAL: u8 = 2;
106
107impl Alarm {
108    /// use to construct alarm timer with duration
109    pub fn with_interval(interval: Duration) -> Self {
110        let trigger = Arc::new(AtomicU8::default());
111
112        let weak_trigger = Arc::downgrade(&trigger);
113        thread::spawn(move || {
114            while let Some(trigger) = weak_trigger.upgrade() {
115                thread::sleep(interval);
116                trigger.store(ALARM_TRIGGER_TIMER, Relaxed);
117            }
118        });
119
120        Self { interval, trigger }
121    }
122
123    /// Returns a closure that allows to manually trigger the alarm
124    ///
125    /// This is useful for cases where more than one alarm even source exists
126    /// In case of `dd` there is the SIGUSR1/SIGINFO case where we want to
127    /// trigger an manual progress report.
128    pub fn manual_trigger_fn(&self) -> Box<dyn Send + Sync + Fn()> {
129        let weak_trigger = Arc::downgrade(&self.trigger);
130        Box::new(move || {
131            if let Some(trigger) = weak_trigger.upgrade() {
132                trigger.store(ALARM_TRIGGER_SIGNAL, Relaxed);
133            }
134        })
135    }
136
137    /// Use this function to poll for any pending alarm event
138    ///
139    /// Returns `ALARM_TRIGGER_NONE` for no pending event.
140    /// Returns `ALARM_TRIGGER_TIMER` if the event was triggered by timer
141    /// Returns `ALARM_TRIGGER_SIGNAL` if the event was triggered manually
142    /// by the closure returned from `manual_trigger_fn`
143    pub fn get_trigger(&self) -> u8 {
144        self.trigger.swap(ALARM_TRIGGER_NONE, Relaxed)
145    }
146
147    // Getter function for the configured interval duration
148    pub fn get_interval(&self) -> Duration {
149        self.interval
150    }
151}
152
153/// A number in blocks or bytes
154///
155/// Some values (seek, skip, iseek, oseek) can have values either in blocks or in bytes.
156/// We need to remember this because the size of the blocks (ibs) is only known after parsing
157/// all the arguments.
158#[derive(Clone, Copy, Debug, PartialEq)]
159enum Num {
160    Blocks(u64),
161    Bytes(u64),
162}
163
164impl Default for Num {
165    fn default() -> Self {
166        Self::Blocks(0)
167    }
168}
169
170impl Num {
171    fn force_bytes_if(self, force: bool) -> Self {
172        match self {
173            Self::Blocks(n) if force => Self::Bytes(n),
174            count => count,
175        }
176    }
177
178    fn to_bytes(self, block_size: u64) -> u64 {
179        match self {
180            Self::Blocks(n) => n * block_size,
181            Self::Bytes(n) => n,
182        }
183    }
184}
185
186/// Data sources.
187///
188/// Use [`Source::stdin_as_file`] if available to enable more
189/// fine-grained access to reading from stdin.
190enum Source {
191    /// Input from stdin.
192    #[cfg(not(unix))]
193    Stdin(io::Stdin),
194
195    /// Input from a file.
196    File(File),
197
198    /// Input from stdin, opened from its file descriptor.
199    #[cfg(unix)]
200    StdinFile(File),
201
202    /// Input from a named pipe, also known as a FIFO.
203    #[cfg(unix)]
204    Fifo(File),
205}
206
207impl Source {
208    /// Create a source from stdin using its raw file descriptor.
209    ///
210    /// This returns an instance of the `Source::StdinFile` variant,
211    /// using the raw file descriptor of [`std::io::Stdin`] to create
212    /// the [`std::fs::File`] parameter. You can use this instead of
213    /// `Source::Stdin` to allow reading from stdin without consuming
214    /// the entire contents of stdin when this process terminates.
215    #[cfg(unix)]
216    fn stdin_as_file() -> Self {
217        let fd = io::stdin().as_raw_fd();
218        let f = unsafe { File::from_raw_fd(fd) };
219        Self::StdinFile(f)
220    }
221
222    /// The length of the data source in number of bytes.
223    ///
224    /// If it cannot be determined, then this function returns 0.
225    fn len(&self) -> io::Result<i64> {
226        #[allow(clippy::match_wildcard_for_single_variants)]
227        match self {
228            Self::File(f) => Ok(f.metadata()?.len().try_into().unwrap_or(i64::MAX)),
229            _ => Ok(0),
230        }
231    }
232
233    fn skip(&mut self, n: u64) -> io::Result<u64> {
234        match self {
235            #[cfg(not(unix))]
236            Self::Stdin(stdin) => match io::copy(&mut stdin.take(n), &mut io::sink()) {
237                Ok(m) if m < n => {
238                    show_error!(
239                        "{}",
240                        translate!("dd-error-cannot-skip-offset", "file" => "standard input")
241                    );
242                    Ok(m)
243                }
244                Ok(m) => Ok(m),
245                Err(e) => Err(e),
246            },
247            #[cfg(unix)]
248            Self::StdinFile(f) => {
249                if let Ok(Some(len)) = try_get_len_of_block_device(f) {
250                    if len < n {
251                        // GNU compatibility:
252                        // this case prints the stats but sets the exit code to 1
253                        show_error!(
254                            "{}",
255                            translate!("dd-error-cannot-skip-invalid", "file" => "standard input")
256                        );
257                        set_exit_code(1);
258                        return Ok(len);
259                    }
260                }
261                match io::copy(&mut f.take(n), &mut io::sink()) {
262                    Ok(m) if m < n => {
263                        show_error!(
264                            "{}",
265                            translate!("dd-error-cannot-skip-offset", "file" => "standard input")
266                        );
267                        Ok(m)
268                    }
269                    Ok(m) => Ok(m),
270                    Err(e) => Err(e),
271                }
272            }
273            Self::File(f) => f.seek(SeekFrom::Current(n.try_into().unwrap())),
274            #[cfg(unix)]
275            Self::Fifo(f) => io::copy(&mut f.take(n), &mut io::sink()),
276        }
277    }
278
279    /// Discard the system file cache for the given portion of the data source.
280    ///
281    /// `offset` and `len` specify a contiguous portion of the data
282    /// source. This function informs the kernel that the specified
283    /// portion of the source is no longer needed. If not possible,
284    /// then this function returns an error.
285    #[cfg(target_os = "linux")]
286    fn discard_cache(&self, offset: libc::off_t, len: libc::off_t) -> nix::Result<()> {
287        #[allow(clippy::match_wildcard_for_single_variants)]
288        match self {
289            Self::File(f) => {
290                let advice = PosixFadviseAdvice::POSIX_FADV_DONTNEED;
291                posix_fadvise(f.as_fd(), offset, len, advice)
292            }
293            _ => Err(Errno::ESPIPE), // "Illegal seek"
294        }
295    }
296}
297
298impl Read for Source {
299    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
300        match self {
301            #[cfg(not(unix))]
302            Self::Stdin(stdin) => stdin.read(buf),
303            Self::File(f) => f.read(buf),
304            #[cfg(unix)]
305            Self::StdinFile(f) => f.read(buf),
306            #[cfg(unix)]
307            Self::Fifo(f) => f.read(buf),
308        }
309    }
310}
311
312/// The source of the data, configured with the given settings.
313///
314/// Use the [`Input::new_stdin`] or [`Input::new_file`] functions to
315/// construct a new instance of this struct. Then pass the instance to
316/// the [`dd_copy`] function to execute the main copy operation
317/// for `dd`.
318struct Input<'a> {
319    /// The source from which bytes will be read.
320    src: Source,
321
322    /// Configuration settings for how to read the data.
323    settings: &'a Settings,
324}
325
326impl<'a> Input<'a> {
327    /// Instantiate this struct with stdin as a source.
328    fn new_stdin(settings: &'a Settings) -> UResult<Self> {
329        #[cfg(not(unix))]
330        let mut src = {
331            let f = File::from(io::stdin().as_handle().try_clone_to_owned()?);
332            let is_file = if let Ok(metadata) = f.metadata() {
333                // this hack is needed as there is no other way on windows
334                // to differentiate between the case where `seek` works
335                // on a file handle or not. i.e. when the handle is no real
336                // file but a pipe, `seek` is still successful, but following
337                // `read`s are not affected by the seek.
338                metadata.creation_time() != 0
339            } else {
340                false
341            };
342            if is_file {
343                Source::File(f)
344            } else {
345                Source::Stdin(io::stdin())
346            }
347        };
348        #[cfg(unix)]
349        let mut src = Source::stdin_as_file();
350        #[cfg(unix)]
351        if let Source::StdinFile(f) = &src {
352            if settings.iflags.directory && !f.metadata()?.is_dir() {
353                return Err(USimpleError::new(
354                    1,
355                    translate!("dd-error-not-directory", "file" => "standard input"),
356                ));
357            }
358        }
359        if settings.skip > 0 {
360            src.skip(settings.skip)?;
361        }
362        Ok(Self { src, settings })
363    }
364
365    /// Instantiate this struct with the named file as a source.
366    fn new_file(filename: &Path, settings: &'a Settings) -> UResult<Self> {
367        let src = {
368            let mut opts = OpenOptions::new();
369            opts.read(true);
370
371            #[cfg(any(target_os = "linux", target_os = "android"))]
372            if let Some(libc_flags) = make_linux_iflags(&settings.iflags) {
373                opts.custom_flags(libc_flags);
374            }
375
376            opts.open(filename).map_err_context(
377                || translate!("dd-error-failed-to-open", "path" => filename.quote()),
378            )?
379        };
380
381        let mut src = Source::File(src);
382        if settings.skip > 0 {
383            src.skip(settings.skip)?;
384        }
385        Ok(Self { src, settings })
386    }
387
388    /// Instantiate this struct with the named pipe as a source.
389    #[cfg(unix)]
390    fn new_fifo(filename: &Path, settings: &'a Settings) -> UResult<Self> {
391        let mut opts = OpenOptions::new();
392        opts.read(true);
393        #[cfg(any(target_os = "linux", target_os = "android"))]
394        opts.custom_flags(make_linux_iflags(&settings.iflags).unwrap_or(0));
395        let mut src = Source::Fifo(opts.open(filename)?);
396        if settings.skip > 0 {
397            src.skip(settings.skip)?;
398        }
399        Ok(Self { src, settings })
400    }
401}
402
403#[cfg(any(target_os = "linux", target_os = "android"))]
404fn make_linux_iflags(iflags: &IFlags) -> Option<libc::c_int> {
405    let mut flag = 0;
406
407    if iflags.direct {
408        flag |= libc::O_DIRECT;
409    }
410    if iflags.directory {
411        flag |= libc::O_DIRECTORY;
412    }
413    if iflags.dsync {
414        flag |= libc::O_DSYNC;
415    }
416    if iflags.noatime {
417        flag |= libc::O_NOATIME;
418    }
419    if iflags.noctty {
420        flag |= libc::O_NOCTTY;
421    }
422    if iflags.nofollow {
423        flag |= libc::O_NOFOLLOW;
424    }
425    if iflags.nonblock {
426        flag |= libc::O_NONBLOCK;
427    }
428    if iflags.sync {
429        flag |= libc::O_SYNC;
430    }
431
432    if flag == 0 { None } else { Some(flag) }
433}
434
435impl Read for Input<'_> {
436    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
437        let mut base_idx = 0;
438        let target_len = buf.len();
439        loop {
440            match self.src.read(&mut buf[base_idx..]) {
441                Ok(0) => return Ok(base_idx),
442                Ok(rlen) if self.settings.iflags.fullblock => {
443                    base_idx += rlen;
444
445                    if base_idx >= target_len {
446                        return Ok(target_len);
447                    }
448                }
449                Ok(len) => return Ok(len),
450                Err(e) if e.kind() == io::ErrorKind::Interrupted => (),
451                Err(_) if self.settings.iconv.noerror => return Ok(base_idx),
452                Err(e) => return Err(e),
453            }
454        }
455    }
456}
457
458impl Input<'_> {
459    /// Discard the system file cache for the given portion of the input.
460    ///
461    /// `offset` and `len` specify a contiguous portion of the input.
462    /// This function informs the kernel that the specified portion of
463    /// the input file is no longer needed. If not possible, then this
464    /// function prints an error message to stderr and sets the exit
465    /// status code to 1.
466    #[cfg_attr(not(target_os = "linux"), allow(clippy::unused_self, unused_variables))]
467    fn discard_cache(&self, offset: libc::off_t, len: libc::off_t) {
468        #[cfg(target_os = "linux")]
469        {
470            show_if_err!(
471                self.src
472                    .discard_cache(offset, len)
473                    .map_err_context(|| translate!("dd-error-failed-discard-cache-input"))
474            );
475        }
476        #[cfg(not(target_os = "linux"))]
477        {
478            // TODO Is there a way to discard filesystem cache on
479            // these other operating systems?
480        }
481    }
482
483    /// Fills a given buffer.
484    /// Reads in increments of 'self.ibs'.
485    /// The start of each ibs-sized read follows the previous one.
486    fn fill_consecutive(&mut self, buf: &mut Vec<u8>) -> io::Result<ReadStat> {
487        let mut reads_complete = 0;
488        let mut reads_partial = 0;
489        let mut bytes_total = 0;
490
491        for chunk in buf.chunks_mut(self.settings.ibs) {
492            match self.read(chunk)? {
493                rlen if rlen == self.settings.ibs => {
494                    bytes_total += rlen;
495                    reads_complete += 1;
496                }
497                rlen if rlen > 0 => {
498                    bytes_total += rlen;
499                    reads_partial += 1;
500                }
501                _ => break,
502            }
503        }
504        buf.truncate(bytes_total);
505        Ok(ReadStat {
506            reads_complete,
507            reads_partial,
508            // Records are not truncated when filling.
509            records_truncated: 0,
510            bytes_total: bytes_total.try_into().unwrap(),
511        })
512    }
513
514    /// Fills a given buffer.
515    /// Reads in increments of 'self.ibs'.
516    /// The start of each ibs-sized read is aligned to multiples of ibs; remaining space is filled with the 'pad' byte.
517    fn fill_blocks(&mut self, buf: &mut Vec<u8>, pad: u8) -> io::Result<ReadStat> {
518        let mut reads_complete = 0;
519        let mut reads_partial = 0;
520        let mut base_idx = 0;
521        let mut bytes_total = 0;
522
523        while base_idx < buf.len() {
524            let next_blk = cmp::min(base_idx + self.settings.ibs, buf.len());
525            let target_len = next_blk - base_idx;
526
527            match self.read(&mut buf[base_idx..next_blk])? {
528                0 => break,
529                rlen if rlen < target_len => {
530                    bytes_total += rlen;
531                    reads_partial += 1;
532                    let padding = vec![pad; target_len - rlen];
533                    buf.splice(base_idx + rlen..next_blk, padding.into_iter());
534                }
535                rlen => {
536                    bytes_total += rlen;
537                    reads_complete += 1;
538                }
539            }
540
541            base_idx += self.settings.ibs;
542        }
543
544        buf.truncate(base_idx);
545        Ok(ReadStat {
546            reads_complete,
547            reads_partial,
548            records_truncated: 0,
549            bytes_total: bytes_total.try_into().unwrap(),
550        })
551    }
552}
553
554enum Density {
555    Sparse,
556    Dense,
557}
558
559/// Data destinations.
560enum Dest {
561    /// Output to stdout.
562    Stdout(Stdout),
563
564    /// Output to a file.
565    ///
566    /// The [`Density`] component indicates whether to attempt to
567    /// write a sparse file when all-zero blocks are encountered.
568    File(File, Density),
569
570    /// Output to a named pipe, also known as a FIFO.
571    #[cfg(unix)]
572    Fifo(File),
573
574    /// Output to nothing, dropping each byte written to the output.
575    #[cfg(unix)]
576    Sink,
577}
578
579impl Dest {
580    fn fsync(&mut self) -> io::Result<()> {
581        match self {
582            Self::Stdout(stdout) => stdout.flush(),
583            Self::File(f, _) => {
584                f.flush()?;
585                f.sync_all()
586            }
587            #[cfg(unix)]
588            Self::Fifo(f) => {
589                f.flush()?;
590                f.sync_all()
591            }
592            #[cfg(unix)]
593            Self::Sink => Ok(()),
594        }
595    }
596
597    fn fdatasync(&mut self) -> io::Result<()> {
598        match self {
599            Self::Stdout(stdout) => stdout.flush(),
600            Self::File(f, _) => {
601                f.flush()?;
602                f.sync_data()
603            }
604            #[cfg(unix)]
605            Self::Fifo(f) => {
606                f.flush()?;
607                f.sync_data()
608            }
609            #[cfg(unix)]
610            Self::Sink => Ok(()),
611        }
612    }
613
614    fn seek(&mut self, n: u64) -> io::Result<u64> {
615        match self {
616            Self::Stdout(stdout) => io::copy(&mut io::repeat(0).take(n), stdout),
617            Self::File(f, _) => {
618                #[cfg(unix)]
619                if let Ok(Some(len)) = try_get_len_of_block_device(f) {
620                    if len < n {
621                        // GNU compatibility:
622                        // this case prints the stats but sets the exit code to 1
623                        show_error!(
624                            "{}",
625                            translate!("dd-error-cannot-seek-invalid", "output" => "standard output")
626                        );
627                        set_exit_code(1);
628                        return Ok(len);
629                    }
630                }
631                f.seek(SeekFrom::Current(n.try_into().unwrap()))
632            }
633            #[cfg(unix)]
634            Self::Fifo(f) => {
635                // Seeking in a named pipe means *reading* from the pipe.
636                io::copy(&mut f.take(n), &mut io::sink())
637            }
638            #[cfg(unix)]
639            Self::Sink => Ok(0),
640        }
641    }
642
643    /// Truncate the underlying file to the current stream position, if possible.
644    fn truncate(&mut self) -> io::Result<()> {
645        #[allow(clippy::match_wildcard_for_single_variants)]
646        match self {
647            Self::File(f, _) => {
648                let pos = f.stream_position()?;
649                f.set_len(pos)
650            }
651            _ => Ok(()),
652        }
653    }
654
655    /// Discard the system file cache for the given portion of the destination.
656    ///
657    /// `offset` and `len` specify a contiguous portion of the
658    /// destination. This function informs the kernel that the
659    /// specified portion of the destination is no longer needed. If
660    /// not possible, then this function returns an error.
661    #[cfg(target_os = "linux")]
662    fn discard_cache(&self, offset: libc::off_t, len: libc::off_t) -> nix::Result<()> {
663        match self {
664            Self::File(f, _) => {
665                let advice = PosixFadviseAdvice::POSIX_FADV_DONTNEED;
666                posix_fadvise(f.as_fd(), offset, len, advice)
667            }
668            _ => Err(Errno::ESPIPE), // "Illegal seek"
669        }
670    }
671
672    /// The length of the data destination in number of bytes.
673    ///
674    /// If it cannot be determined, then this function returns 0.
675    fn len(&self) -> io::Result<i64> {
676        #[allow(clippy::match_wildcard_for_single_variants)]
677        match self {
678            Self::File(f, _) => Ok(f.metadata()?.len().try_into().unwrap_or(i64::MAX)),
679            _ => Ok(0),
680        }
681    }
682}
683
684/// Decide whether the given buffer is all zeros.
685fn is_sparse(buf: &[u8]) -> bool {
686    buf.iter().all(|&e| e == 0u8)
687}
688
689impl Write for Dest {
690    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
691        match self {
692            Self::File(f, Density::Sparse) if is_sparse(buf) => {
693                let seek_amt: i64 = buf
694                    .len()
695                    .try_into()
696                    .expect("Internal dd Error: Seek amount greater than signed 64-bit integer");
697                f.seek(SeekFrom::Current(seek_amt))?;
698                Ok(buf.len())
699            }
700            Self::File(f, _) => f.write(buf),
701            Self::Stdout(stdout) => stdout.write(buf),
702            #[cfg(unix)]
703            Self::Fifo(f) => f.write(buf),
704            #[cfg(unix)]
705            Self::Sink => Ok(buf.len()),
706        }
707    }
708
709    fn flush(&mut self) -> io::Result<()> {
710        match self {
711            Self::Stdout(stdout) => stdout.flush(),
712            Self::File(f, _) => f.flush(),
713            #[cfg(unix)]
714            Self::Fifo(f) => f.flush(),
715            #[cfg(unix)]
716            Self::Sink => Ok(()),
717        }
718    }
719}
720
721/// The destination of the data, configured with the given settings.
722///
723/// Use the [`Output::new_stdout`] or [`Output::new_file`] functions
724/// to construct a new instance of this struct. Then use the
725/// [`dd_copy`] function to execute the main copy operation for
726/// `dd`.
727struct Output<'a> {
728    /// The destination to which bytes will be written.
729    dst: Dest,
730
731    /// Configuration settings for how to read and write the data.
732    settings: &'a Settings,
733}
734
735impl<'a> Output<'a> {
736    /// Instantiate this struct with stdout as a destination.
737    fn new_stdout(settings: &'a Settings) -> UResult<Self> {
738        let mut dst = Dest::Stdout(io::stdout());
739        dst.seek(settings.seek)
740            .map_err_context(|| translate!("dd-error-write-error"))?;
741        Ok(Self { dst, settings })
742    }
743
744    /// Instantiate this struct with the named file as a destination.
745    fn new_file(filename: &Path, settings: &'a Settings) -> UResult<Self> {
746        fn open_dst(path: &Path, cflags: &OConvFlags, oflags: &OFlags) -> Result<File, io::Error> {
747            let mut opts = OpenOptions::new();
748            opts.write(true)
749                .create(!cflags.nocreat)
750                .create_new(cflags.excl)
751                .append(oflags.append);
752
753            #[cfg(any(target_os = "linux", target_os = "android"))]
754            if let Some(libc_flags) = make_linux_oflags(oflags) {
755                opts.custom_flags(libc_flags);
756            }
757
758            opts.open(path)
759        }
760
761        let dst = open_dst(filename, &settings.oconv, &settings.oflags).map_err_context(
762            || translate!("dd-error-failed-to-open", "path" => filename.quote()),
763        )?;
764
765        // Seek to the index in the output file, truncating if requested.
766        //
767        // Calling `set_len()` may result in an error (for example,
768        // when calling it on `/dev/null`), but we don't want to
769        // terminate the process when that happens.  Instead, we
770        // suppress the error by calling `Result::ok()`. This matches
771        // the behavior of GNU `dd` when given the command-line
772        // argument `of=/dev/null`.
773        if !settings.oconv.notrunc {
774            dst.set_len(settings.seek).ok();
775        }
776
777        Self::prepare_file(dst, settings)
778    }
779
780    fn prepare_file(dst: File, settings: &'a Settings) -> UResult<Self> {
781        let density = if settings.oconv.sparse {
782            Density::Sparse
783        } else {
784            Density::Dense
785        };
786        let mut dst = Dest::File(dst, density);
787        dst.seek(settings.seek)
788            .map_err_context(|| translate!("dd-error-failed-to-seek"))?;
789        Ok(Self { dst, settings })
790    }
791
792    /// Instantiate this struct with file descriptor as a destination.
793    ///
794    /// This is useful e.g. for the case when the file descriptor was
795    /// already opened by the system (stdout) and has a state
796    /// (current position) that shall be used.
797    fn new_file_from_stdout(settings: &'a Settings) -> UResult<Self> {
798        let fx = OwnedFileDescriptorOrHandle::from(io::stdout())?;
799        #[cfg(any(target_os = "linux", target_os = "android"))]
800        if let Some(libc_flags) = make_linux_oflags(&settings.oflags) {
801            nix::fcntl::fcntl(
802                fx.as_raw().as_fd(),
803                F_SETFL(OFlag::from_bits_retain(libc_flags)),
804            )?;
805        }
806
807        Self::prepare_file(fx.into_file(), settings)
808    }
809
810    /// Instantiate this struct with the given named pipe as a destination.
811    #[cfg(unix)]
812    fn new_fifo(filename: &Path, settings: &'a Settings) -> UResult<Self> {
813        // We simulate seeking in a FIFO by *reading*, so we open the
814        // file for reading. But then we need to close the file and
815        // re-open it for writing.
816        if settings.seek > 0 {
817            Dest::Fifo(File::open(filename)?).seek(settings.seek)?;
818        }
819        // If `count=0`, then we don't bother opening the file for
820        // writing because that would cause this process to block
821        // indefinitely.
822        if let Some(Num::Blocks(0) | Num::Bytes(0)) = settings.count {
823            let dst = Dest::Sink;
824            return Ok(Self { dst, settings });
825        }
826        // At this point, we know there is at least one block to write
827        // to the output, so we open the file for writing.
828        let mut opts = OpenOptions::new();
829        opts.write(true)
830            .create(!settings.oconv.nocreat)
831            .create_new(settings.oconv.excl)
832            .append(settings.oflags.append);
833        #[cfg(any(target_os = "linux", target_os = "android"))]
834        opts.custom_flags(make_linux_oflags(&settings.oflags).unwrap_or(0));
835        let dst = Dest::Fifo(opts.open(filename)?);
836        Ok(Self { dst, settings })
837    }
838
839    /// Discard the system file cache for the given portion of the output.
840    ///
841    /// `offset` and `len` specify a contiguous portion of the output.
842    /// This function informs the kernel that the specified portion of
843    /// the output file is no longer needed. If not possible, then
844    /// this function prints an error message to stderr and sets the
845    /// exit status code to 1.
846    #[cfg_attr(not(target_os = "linux"), allow(clippy::unused_self, unused_variables))]
847    fn discard_cache(&self, offset: libc::off_t, len: libc::off_t) {
848        #[cfg(target_os = "linux")]
849        {
850            show_if_err!(
851                self.dst
852                    .discard_cache(offset, len)
853                    .map_err_context(|| { translate!("dd-error-failed-discard-cache-output") })
854            );
855        }
856        #[cfg(not(target_os = "linux"))]
857        {
858            // TODO Is there a way to discard filesystem cache on
859            // these other operating systems?
860        }
861    }
862
863    /// writes a block of data. optionally retries when first try didn't complete
864    ///
865    /// this is needed by gnu-test: tests/dd/stats.s
866    /// the write can be interrupted by a system signal.
867    /// e.g. SIGUSR1 which is send to report status
868    /// without retry, the data might not be fully written to destination.
869    fn write_block(&mut self, chunk: &[u8]) -> io::Result<usize> {
870        let full_len = chunk.len();
871        let mut base_idx = 0;
872        loop {
873            match self.dst.write(&chunk[base_idx..]) {
874                Ok(wlen) => {
875                    base_idx += wlen;
876                    // take iflags.fullblock as oflags shall not have this option
877                    if (base_idx >= full_len) || !self.settings.iflags.fullblock {
878                        return Ok(base_idx);
879                    }
880                }
881                Err(e) if e.kind() == io::ErrorKind::Interrupted => (),
882                Err(e) => return Err(e),
883            }
884        }
885    }
886
887    /// Write the given bytes one block at a time.
888    ///
889    /// This may write partial blocks (for example, if the underlying
890    /// call to [`Write::write`] writes fewer than `buf.len()`
891    /// bytes). The returned [`WriteStat`] object will include the
892    /// number of partial and complete blocks written during execution
893    /// of this function.
894    fn write_blocks(&mut self, buf: &[u8]) -> io::Result<WriteStat> {
895        let mut writes_complete = 0;
896        let mut writes_partial = 0;
897        let mut bytes_total = 0;
898
899        for chunk in buf.chunks(self.settings.obs) {
900            let wlen = self.write_block(chunk)?;
901            if wlen < self.settings.obs {
902                writes_partial += 1;
903            } else {
904                writes_complete += 1;
905            }
906            bytes_total += wlen;
907        }
908
909        Ok(WriteStat {
910            writes_complete,
911            writes_partial,
912            bytes_total: bytes_total.try_into().unwrap_or(0u128),
913        })
914    }
915
916    /// Flush the output to disk, if configured to do so.
917    fn sync(&mut self) -> io::Result<()> {
918        if self.settings.oconv.fsync {
919            self.dst.fsync()
920        } else if self.settings.oconv.fdatasync {
921            self.dst.fdatasync()
922        } else {
923            // Intentionally do nothing in this case.
924            Ok(())
925        }
926    }
927
928    /// Truncate the underlying file to the current stream position, if possible.
929    fn truncate(&mut self) -> io::Result<()> {
930        self.dst.truncate()
931    }
932}
933
934/// The block writer either with or without partial block buffering.
935enum BlockWriter<'a> {
936    /// Block writer with partial block buffering.
937    ///
938    /// Partial blocks are buffered until completed.
939    Buffered(BufferedOutput<'a>),
940
941    /// Block writer without partial block buffering.
942    ///
943    /// Partial blocks are written immediately.
944    Unbuffered(Output<'a>),
945}
946
947impl BlockWriter<'_> {
948    fn discard_cache(&self, offset: libc::off_t, len: libc::off_t) {
949        match self {
950            Self::Unbuffered(o) => o.discard_cache(offset, len),
951            Self::Buffered(o) => o.discard_cache(offset, len),
952        }
953    }
954
955    fn flush(&mut self) -> io::Result<WriteStat> {
956        match self {
957            Self::Unbuffered(_) => Ok(WriteStat::default()),
958            Self::Buffered(o) => o.flush(),
959        }
960    }
961
962    fn sync(&mut self) -> io::Result<()> {
963        match self {
964            Self::Unbuffered(o) => o.sync(),
965            Self::Buffered(o) => o.sync(),
966        }
967    }
968
969    /// Truncate the file to the final cursor location.
970    fn truncate(&mut self) {
971        // Calling `set_len()` may result in an error (for example,
972        // when calling it on `/dev/null`), but we don't want to
973        // terminate the process when that happens. Instead, we
974        // suppress the error by calling `Result::ok()`. This matches
975        // the behavior of GNU `dd` when given the command-line
976        // argument `of=/dev/null`.
977        match self {
978            Self::Unbuffered(o) => o.truncate().ok(),
979            Self::Buffered(o) => o.truncate().ok(),
980        };
981    }
982
983    fn write_blocks(&mut self, buf: &[u8]) -> io::Result<WriteStat> {
984        match self {
985            Self::Unbuffered(o) => o.write_blocks(buf),
986            Self::Buffered(o) => o.write_blocks(buf),
987        }
988    }
989}
990
991/// depending on the command line arguments, this function
992/// informs the OS to flush/discard the caches for input and/or output file.
993fn flush_caches_full_length(i: &Input, o: &Output) -> io::Result<()> {
994    // TODO Better error handling for overflowing `len`.
995    if i.settings.iflags.nocache {
996        let offset = 0;
997        #[allow(clippy::useless_conversion)]
998        let len = i.src.len()?.try_into().unwrap();
999        i.discard_cache(offset, len);
1000    }
1001    // Similarly, discard the system cache for the output file.
1002    //
1003    // TODO Better error handling for overflowing `len`.
1004    if i.settings.oflags.nocache {
1005        let offset = 0;
1006        #[allow(clippy::useless_conversion)]
1007        let len = o.dst.len()?.try_into().unwrap();
1008        o.discard_cache(offset, len);
1009    }
1010
1011    Ok(())
1012}
1013
1014/// Copy the given input data to this output, consuming both.
1015///
1016/// This method contains the main loop for the `dd` program. Bytes
1017/// are read in blocks from `i` and written in blocks to this
1018/// output. Read/write statistics are reported to stderr as
1019/// configured by the `status` command-line argument.
1020///
1021/// # Errors
1022///
1023/// If there is a problem reading from the input or writing to
1024/// this output.
1025fn dd_copy(mut i: Input, o: Output) -> io::Result<()> {
1026    // The read and write statistics.
1027    //
1028    // These objects are counters, initialized to zero. After each
1029    // iteration of the main loop, each will be incremented by the
1030    // number of blocks read and written, respectively.
1031    let mut rstat = ReadStat::default();
1032    let mut wstat = WriteStat::default();
1033
1034    // The time at which the main loop starts executing.
1035    //
1036    // When `status=progress` is given on the command-line, the
1037    // `dd` program reports its progress every second or so. Part
1038    // of its report includes the throughput in bytes per second,
1039    // which requires knowing how long the process has been
1040    // running.
1041    let start = Instant::now();
1042
1043    // A good buffer size for reading.
1044    //
1045    // This is an educated guess about a good buffer size based on
1046    // the input and output block sizes.
1047    let bsize = calc_bsize(i.settings.ibs, o.settings.obs);
1048
1049    // Start a thread that reports transfer progress.
1050    //
1051    // The `dd` program reports its progress after every block is written,
1052    // at most every 1 second, and only if `status=progress` is given on
1053    // the command-line or a SIGUSR1 signal is received. We
1054    // perform this reporting in a new thread so as not to take
1055    // any CPU time away from the actual reading and writing of
1056    // data. We send a `ProgUpdate` from the transmitter `prog_tx`
1057    // to the receives `rx`, and the receiver prints the transfer
1058    // information.
1059    let (prog_tx, rx) = mpsc::channel();
1060    let output_thread = thread::spawn(gen_prog_updater(rx, i.settings.status));
1061
1062    // Whether to truncate the output file after all blocks have been written.
1063    let truncate = !o.settings.oconv.notrunc;
1064
1065    // Optimization: if no blocks are to be written, then don't
1066    // bother allocating any buffers.
1067    if let Some(Num::Blocks(0) | Num::Bytes(0)) = i.settings.count {
1068        // Even though we are not reading anything from the input
1069        // file, we still need to honor the `nocache` flag, which
1070        // requests that we inform the system that we no longer
1071        // need the contents of the input file in a system cache.
1072        //
1073        flush_caches_full_length(&i, &o)?;
1074        return finalize(
1075            BlockWriter::Unbuffered(o),
1076            rstat,
1077            wstat,
1078            start,
1079            &prog_tx,
1080            output_thread,
1081            truncate,
1082        );
1083    }
1084
1085    // Create a common buffer with a capacity of the block size.
1086    // This is the max size needed.
1087    let mut buf = vec![BUF_INIT_BYTE; bsize];
1088
1089    // Spawn a timer thread to provide a scheduled signal indicating when we
1090    // should send an update of our progress to the reporting thread.
1091    //
1092    // This avoids the need to query the OS monotonic clock for every block.
1093    let alarm = Alarm::with_interval(Duration::from_secs(1));
1094
1095    // The signal handler spawns an own thread that waits for signals.
1096    // When the signal is received, it calls a handler function.
1097    // We inject a handler function that manually triggers the alarm.
1098    #[cfg(target_os = "linux")]
1099    let signal_handler = progress::SignalHandler::install_signal_handler(alarm.manual_trigger_fn());
1100    #[cfg(target_os = "linux")]
1101    if let Err(e) = &signal_handler {
1102        if Some(StatusLevel::None) != i.settings.status {
1103            eprintln!("{}\n\t{e}", translate!("dd-warning-signal-handler"));
1104        }
1105    }
1106
1107    // Index in the input file where we are reading bytes and in
1108    // the output file where we are writing bytes.
1109    //
1110    // These are updated on each iteration of the main loop.
1111    let mut read_offset = 0;
1112    let mut write_offset = 0;
1113
1114    let input_nocache = i.settings.iflags.nocache;
1115    let output_nocache = o.settings.oflags.nocache;
1116
1117    // Add partial block buffering, if needed.
1118    let mut o = if o.settings.buffered {
1119        BlockWriter::Buffered(BufferedOutput::new(o))
1120    } else {
1121        BlockWriter::Unbuffered(o)
1122    };
1123
1124    // The main read/write loop.
1125    //
1126    // Each iteration reads blocks from the input and writes
1127    // blocks to this output. Read/write statistics are updated on
1128    // each iteration and cumulative statistics are reported to
1129    // the progress reporting thread.
1130    while below_count_limit(i.settings.count, &rstat) {
1131        // Read a block from the input then write the block to the output.
1132        //
1133        // As an optimization, make an educated guess about the
1134        // best buffer size for reading based on the number of
1135        // blocks already read and the number of blocks remaining.
1136        let loop_bsize = calc_loop_bsize(i.settings.count, &rstat, &wstat, i.settings.ibs, bsize);
1137        let rstat_update = read_helper(&mut i, &mut buf, loop_bsize)?;
1138        if rstat_update.is_empty() {
1139            break;
1140        }
1141        let wstat_update = o.write_blocks(&buf)?;
1142
1143        // Discard the system file cache for the read portion of
1144        // the input file.
1145        //
1146        // TODO Better error handling for overflowing `offset` and `len`.
1147        let read_len = rstat_update.bytes_total;
1148        if input_nocache {
1149            let offset = read_offset.try_into().unwrap();
1150            let len = read_len.try_into().unwrap();
1151            i.discard_cache(offset, len);
1152        }
1153        read_offset += read_len;
1154
1155        // Discard the system file cache for the written portion
1156        // of the output file.
1157        //
1158        // TODO Better error handling for overflowing `offset` and `len`.
1159        let write_len = wstat_update.bytes_total;
1160        if output_nocache {
1161            let offset = write_offset.try_into().unwrap();
1162            let len = write_len.try_into().unwrap();
1163            o.discard_cache(offset, len);
1164        }
1165        write_offset += write_len;
1166
1167        // Update the read/write stats and inform the progress thread once per second.
1168        //
1169        // If the receiver is disconnected, `send()` returns an
1170        // error. Since it is just reporting progress and is not
1171        // crucial to the operation of `dd`, let's just ignore the
1172        // error.
1173        rstat += rstat_update;
1174        wstat += wstat_update;
1175        match alarm.get_trigger() {
1176            ALARM_TRIGGER_NONE => {}
1177            t @ (ALARM_TRIGGER_TIMER | ALARM_TRIGGER_SIGNAL) => {
1178                let tp = match t {
1179                    ALARM_TRIGGER_TIMER => ProgUpdateType::Periodic,
1180                    _ => ProgUpdateType::Signal,
1181                };
1182                let prog_update = ProgUpdate::new(rstat, wstat, start.elapsed(), tp);
1183                prog_tx.send(prog_update).unwrap_or(());
1184            }
1185            _ => {}
1186        }
1187    }
1188
1189    finalize(o, rstat, wstat, start, &prog_tx, output_thread, truncate)
1190}
1191
1192/// Flush output, print final stats, and join with the progress thread.
1193fn finalize<T>(
1194    mut output: BlockWriter,
1195    rstat: ReadStat,
1196    wstat: WriteStat,
1197    start: Instant,
1198    prog_tx: &mpsc::Sender<ProgUpdate>,
1199    output_thread: thread::JoinHandle<T>,
1200    truncate: bool,
1201) -> io::Result<()> {
1202    // Flush the output in case a partial write has been buffered but
1203    // not yet written.
1204    let wstat_update = output.flush()?;
1205
1206    // Sync the output, if configured to do so.
1207    output.sync()?;
1208
1209    // Truncate the file to the final cursor location.
1210    if truncate {
1211        output.truncate();
1212    }
1213
1214    // Print the final read/write statistics.
1215    let wstat = wstat + wstat_update;
1216    let prog_update = ProgUpdate::new(rstat, wstat, start.elapsed(), ProgUpdateType::Final);
1217    prog_tx.send(prog_update).unwrap_or(());
1218    // Wait for the output thread to finish
1219    output_thread
1220        .join()
1221        .expect("Failed to join with the output thread.");
1222
1223    Ok(())
1224}
1225
1226#[cfg(any(target_os = "linux", target_os = "android"))]
1227#[allow(clippy::cognitive_complexity)]
1228fn make_linux_oflags(oflags: &OFlags) -> Option<libc::c_int> {
1229    let mut flag = 0;
1230
1231    // oflag=FLAG
1232    if oflags.append {
1233        flag |= libc::O_APPEND;
1234    }
1235    if oflags.direct {
1236        flag |= libc::O_DIRECT;
1237    }
1238    if oflags.directory {
1239        flag |= libc::O_DIRECTORY;
1240    }
1241    if oflags.dsync {
1242        flag |= libc::O_DSYNC;
1243    }
1244    if oflags.noatime {
1245        flag |= libc::O_NOATIME;
1246    }
1247    if oflags.noctty {
1248        flag |= libc::O_NOCTTY;
1249    }
1250    if oflags.nofollow {
1251        flag |= libc::O_NOFOLLOW;
1252    }
1253    if oflags.nonblock {
1254        flag |= libc::O_NONBLOCK;
1255    }
1256    if oflags.sync {
1257        flag |= libc::O_SYNC;
1258    }
1259
1260    if flag == 0 { None } else { Some(flag) }
1261}
1262
1263/// Read from an input (that is, a source of bytes) into the given buffer.
1264///
1265/// This function also performs any conversions as specified by
1266/// `conv=swab` or `conv=block` command-line arguments. This function
1267/// mutates the `buf` argument in-place. The returned [`ReadStat`]
1268/// indicates how many blocks were read.
1269fn read_helper(i: &mut Input, buf: &mut Vec<u8>, bsize: usize) -> io::Result<ReadStat> {
1270    // Local Helper Fns -------------------------------------------------
1271    fn perform_swab(buf: &mut [u8]) {
1272        for base in (1..buf.len()).step_by(2) {
1273            buf.swap(base, base - 1);
1274        }
1275    }
1276    // ------------------------------------------------------------------
1277    // Read
1278    // Resize the buffer to the bsize. Any garbage data in the buffer is overwritten or truncated, so there is no need to fill with BUF_INIT_BYTE first.
1279    buf.resize(bsize, BUF_INIT_BYTE);
1280
1281    let mut rstat = match i.settings.iconv.sync {
1282        Some(ch) => i.fill_blocks(buf, ch)?,
1283        _ => i.fill_consecutive(buf)?,
1284    };
1285    // Return early if no data
1286    if rstat.reads_complete == 0 && rstat.reads_partial == 0 {
1287        return Ok(rstat);
1288    }
1289
1290    // Perform any conv=x[,x...] options
1291    if i.settings.iconv.swab {
1292        perform_swab(buf);
1293    }
1294
1295    match i.settings.iconv.mode {
1296        Some(ref mode) => {
1297            *buf = conv_block_unblock_helper(buf.clone(), mode, &mut rstat);
1298            Ok(rstat)
1299        }
1300        None => Ok(rstat),
1301    }
1302}
1303
1304// Calculate a 'good' internal buffer size.
1305// For performance of the read/write functions, the buffer should hold
1306// both an integral number of reads and an integral number of writes. For
1307// sane real-world memory use, it should not be too large. I believe
1308// the least common multiple is a good representation of these interests.
1309// https://en.wikipedia.org/wiki/Least_common_multiple#Using_the_greatest_common_divisor
1310fn calc_bsize(ibs: usize, obs: usize) -> usize {
1311    let gcd = Gcd::gcd(ibs, obs);
1312    // calculate the lcm from gcd
1313    (ibs / gcd) * obs
1314}
1315
1316/// Calculate the buffer size appropriate for this loop iteration, respecting
1317/// a `count=N` if present.
1318fn calc_loop_bsize(
1319    count: Option<Num>,
1320    rstat: &ReadStat,
1321    wstat: &WriteStat,
1322    ibs: usize,
1323    ideal_bsize: usize,
1324) -> usize {
1325    match count {
1326        Some(Num::Blocks(rmax)) => {
1327            let rsofar = rstat.reads_complete + rstat.reads_partial;
1328            let rremain = rmax - rsofar;
1329            cmp::min(ideal_bsize as u64, rremain * ibs as u64) as usize
1330        }
1331        Some(Num::Bytes(bmax)) => {
1332            let bmax: u128 = bmax.into();
1333            let bremain: u128 = bmax - wstat.bytes_total;
1334            cmp::min(ideal_bsize as u128, bremain) as usize
1335        }
1336        None => ideal_bsize,
1337    }
1338}
1339
1340/// Decide if the current progress is below a `count=N` limit or return
1341/// `true` if no such limit is set.
1342fn below_count_limit(count: Option<Num>, rstat: &ReadStat) -> bool {
1343    match count {
1344        Some(Num::Blocks(n)) => rstat.reads_complete + rstat.reads_partial < n,
1345        Some(Num::Bytes(n)) => rstat.bytes_total < n,
1346        None => true,
1347    }
1348}
1349
1350/// Canonicalized file name of `/dev/stdout`.
1351///
1352/// For example, if this process were invoked from the command line as
1353/// `dd`, then this function returns the [`OsString`] form of
1354/// `"/dev/stdout"`. However, if this process were invoked as `dd >
1355/// outfile`, then this function returns the canonicalized path to
1356/// `outfile`, something like `"/path/to/outfile"`.
1357fn stdout_canonicalized() -> OsString {
1358    match Path::new("/dev/stdout").canonicalize() {
1359        Ok(p) => p.into_os_string(),
1360        Err(_) => OsString::from("/dev/stdout"),
1361    }
1362}
1363
1364/// Decide whether stdout is being redirected to a seekable file.
1365///
1366/// For example, if this process were invoked from the command line as
1367///
1368/// ```sh
1369/// dd if=/dev/zero bs=1 count=10 seek=5 > /dev/sda1
1370/// ```
1371///
1372/// where `/dev/sda1` is a seekable block device then this function
1373/// would return true. If invoked as
1374///
1375/// ```sh
1376/// dd if=/dev/zero bs=1 count=10 seek=5
1377/// ```
1378///
1379/// then this function would return false.
1380fn is_stdout_redirected_to_seekable_file() -> bool {
1381    let s = stdout_canonicalized();
1382    let p = Path::new(&s);
1383    match File::open(p) {
1384        Ok(mut f) => {
1385            f.stream_position().is_ok() && f.seek(SeekFrom::End(0)).is_ok() && f.rewind().is_ok()
1386        }
1387        Err(_) => false,
1388    }
1389}
1390
1391/// Try to get the len if it is a block device
1392#[cfg(unix)]
1393fn try_get_len_of_block_device(file: &mut File) -> io::Result<Option<u64>> {
1394    let ftype = file.metadata()?.file_type();
1395    if !ftype.is_block_device() {
1396        return Ok(None);
1397    }
1398
1399    // FIXME: this can be replaced by file.stream_len() when stable.
1400    let len = file.seek(SeekFrom::End(0))?;
1401    file.rewind()?;
1402    Ok(Some(len))
1403}
1404
1405/// Decide whether the named file is a named pipe, also known as a FIFO.
1406#[cfg(unix)]
1407fn is_fifo(filename: &str) -> bool {
1408    if let Ok(metadata) = std::fs::metadata(filename) {
1409        if metadata.file_type().is_fifo() {
1410            return true;
1411        }
1412    }
1413    false
1414}
1415
1416#[uucore::main]
1417pub fn uumain(args: impl uucore::Args) -> UResult<()> {
1418    let matches = uucore::clap_localization::handle_clap_result(uu_app(), args)?;
1419
1420    let settings: Settings = Parser::new().parse(
1421        matches
1422            .get_many::<String>(options::OPERANDS)
1423            .unwrap_or_default(),
1424    )?;
1425
1426    let i = match settings.infile {
1427        #[cfg(unix)]
1428        Some(ref infile) if is_fifo(infile) => Input::new_fifo(Path::new(&infile), &settings)?,
1429        Some(ref infile) => Input::new_file(Path::new(&infile), &settings)?,
1430        None => Input::new_stdin(&settings)?,
1431    };
1432    let o = match settings.outfile {
1433        #[cfg(unix)]
1434        Some(ref outfile) if is_fifo(outfile) => Output::new_fifo(Path::new(&outfile), &settings)?,
1435        Some(ref outfile) => Output::new_file(Path::new(&outfile), &settings)?,
1436        None if is_stdout_redirected_to_seekable_file() => Output::new_file_from_stdout(&settings)?,
1437        None => Output::new_stdout(&settings)?,
1438    };
1439    dd_copy(i, o).map_err_context(|| translate!("dd-error-io-error"))
1440}
1441
1442pub fn uu_app() -> Command {
1443    Command::new(uucore::util_name())
1444        .version(uucore::crate_version!())
1445        .help_template(uucore::localized_help_template(uucore::util_name()))
1446        .about(translate!("dd-about"))
1447        .override_usage(format_usage(&translate!("dd-usage")))
1448        .after_help(translate!("dd-after-help"))
1449        .infer_long_args(true)
1450        .arg(Arg::new(options::OPERANDS).num_args(1..))
1451}
1452
1453#[cfg(test)]
1454mod tests {
1455    use crate::{Output, Parser, calc_bsize};
1456
1457    use std::path::Path;
1458
1459    #[test]
1460    fn bsize_test_primes() {
1461        let (n, m) = (7901, 7919);
1462        let res = calc_bsize(n, m);
1463        assert_eq!(res % n, 0);
1464        assert_eq!(res % m, 0);
1465
1466        assert_eq!(res, n * m);
1467    }
1468
1469    #[test]
1470    fn bsize_test_rel_prime_obs_greater() {
1471        let (n, m) = (7 * 5119, 13 * 5119);
1472        let res = calc_bsize(n, m);
1473        assert_eq!(res % n, 0);
1474        assert_eq!(res % m, 0);
1475
1476        assert_eq!(res, 7 * 13 * 5119);
1477    }
1478
1479    #[test]
1480    fn bsize_test_rel_prime_ibs_greater() {
1481        let (n, m) = (13 * 5119, 7 * 5119);
1482        let res = calc_bsize(n, m);
1483        assert_eq!(res % n, 0);
1484        assert_eq!(res % m, 0);
1485
1486        assert_eq!(res, 7 * 13 * 5119);
1487    }
1488
1489    #[test]
1490    fn bsize_test_3fac_rel_prime() {
1491        let (n, m) = (11 * 13 * 5119, 7 * 11 * 5119);
1492        let res = calc_bsize(n, m);
1493        assert_eq!(res % n, 0);
1494        assert_eq!(res % m, 0);
1495
1496        assert_eq!(res, 7 * 11 * 13 * 5119);
1497    }
1498
1499    #[test]
1500    fn bsize_test_ibs_greater() {
1501        let (n, m) = (512 * 1024, 256 * 1024);
1502        let res = calc_bsize(n, m);
1503        assert_eq!(res % n, 0);
1504        assert_eq!(res % m, 0);
1505
1506        assert_eq!(res, n);
1507    }
1508
1509    #[test]
1510    fn bsize_test_obs_greater() {
1511        let (n, m) = (256 * 1024, 512 * 1024);
1512        let res = calc_bsize(n, m);
1513        assert_eq!(res % n, 0);
1514        assert_eq!(res % m, 0);
1515
1516        assert_eq!(res, m);
1517    }
1518
1519    #[test]
1520    fn bsize_test_bs_eq() {
1521        let (n, m) = (1024, 1024);
1522        let res = calc_bsize(n, m);
1523        assert_eq!(res % n, 0);
1524        assert_eq!(res % m, 0);
1525
1526        assert_eq!(res, m);
1527    }
1528
1529    #[test]
1530    fn test_nocreat_causes_failure_when_ofile_doesnt_exist() {
1531        let args = &["conv=nocreat", "of=not-a-real.file"];
1532        let settings = Parser::new().parse(args).unwrap();
1533        assert!(
1534            Output::new_file(Path::new(settings.outfile.as_ref().unwrap()), &settings).is_err()
1535        );
1536    }
1537}