diff options
Diffstat (limited to 'src/dd.c')
-rw-r--r-- | src/dd.c | 2570 |
1 files changed, 2570 insertions, 0 deletions
diff --git a/src/dd.c b/src/dd.c new file mode 100644 index 0000000..cfafb25 --- /dev/null +++ b/src/dd.c @@ -0,0 +1,2570 @@ +/* dd -- convert a file while copying it. + Copyright (C) 1985-2022 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. */ + +/* Written by Paul Rubin, David MacKenzie, and Stuart Kemp. */ + +#include <config.h> + +#include <sys/types.h> +#include <signal.h> + +#include "system.h" +#include "alignalloc.h" +#include "close-stream.h" +#include "die.h" +#include "error.h" +#include "fd-reopen.h" +#include "gethrxtime.h" +#include "human.h" +#include "ioblksize.h" +#include "long-options.h" +#include "quote.h" +#include "verror.h" +#include "xstrtol.h" +#include "xtime.h" + +/* The official name of this program (e.g., no 'g' prefix). */ +#define PROGRAM_NAME "dd" + +#define AUTHORS \ + proper_name ("Paul Rubin"), \ + proper_name ("David MacKenzie"), \ + proper_name ("Stuart Kemp") + +/* Use SA_NOCLDSTOP as a proxy for whether the sigaction machinery is + present. */ +#ifndef SA_NOCLDSTOP +# define SA_NOCLDSTOP 0 +# define sigprocmask(How, Set, Oset) /* empty */ +# define sigset_t int +# if ! HAVE_SIGINTERRUPT +# define siginterrupt(sig, flag) /* empty */ +# endif +#endif + +/* NonStop circa 2011 lacks SA_RESETHAND; see Bug#9076. */ +#ifndef SA_RESETHAND +# define SA_RESETHAND 0 +#endif + +#ifndef SIGINFO +# define SIGINFO SIGUSR1 +#endif + +/* This may belong in GNULIB's fcntl module instead. + Define O_CIO to 0 if it is not supported by this OS. */ +#ifndef O_CIO +# define O_CIO 0 +#endif + +/* On AIX 5.1 and AIX 5.2, O_NOCACHE is defined via <fcntl.h> + and would interfere with our use of that name, below. */ +#undef O_NOCACHE + +#if ! HAVE_FDATASYNC +# define fdatasync(fd) (errno = ENOSYS, -1) +#endif + +#define output_char(c) \ + do \ + { \ + obuf[oc++] = (c); \ + if (oc >= output_blocksize) \ + write_output (); \ + } \ + while (0) + +/* Default input and output blocksize. */ +#define DEFAULT_BLOCKSIZE 512 + +/* Conversions bit masks. */ +enum + { + C_ASCII = 01, + + C_EBCDIC = 02, + C_IBM = 04, + C_BLOCK = 010, + C_UNBLOCK = 020, + C_LCASE = 040, + C_UCASE = 0100, + C_SWAB = 0200, + C_NOERROR = 0400, + C_NOTRUNC = 01000, + C_SYNC = 02000, + + /* Use separate input and output buffers, and combine partial + input blocks. */ + C_TWOBUFS = 04000, + + C_NOCREAT = 010000, + C_EXCL = 020000, + C_FDATASYNC = 040000, + C_FSYNC = 0100000, + + C_SPARSE = 0200000 + }; + +/* Status levels. */ +enum + { + STATUS_NONE = 1, + STATUS_NOXFER = 2, + STATUS_DEFAULT = 3, + STATUS_PROGRESS = 4 + }; + +/* The name of the input file, or NULL for the standard input. */ +static char const *input_file = NULL; + +/* The name of the output file, or NULL for the standard output. */ +static char const *output_file = NULL; + +/* The page size on this host. */ +static idx_t page_size; + +/* The number of bytes in which atomic reads are done. */ +static idx_t input_blocksize = 0; + +/* The number of bytes in which atomic writes are done. */ +static idx_t output_blocksize = 0; + +/* Conversion buffer size, in bytes. 0 prevents conversions. */ +static idx_t conversion_blocksize = 0; + +/* Skip this many records of 'input_blocksize' bytes before input. */ +static intmax_t skip_records = 0; + +/* Skip this many bytes before input in addition of 'skip_records' + records. */ +static idx_t skip_bytes = 0; + +/* Skip this many records of 'output_blocksize' bytes before output. */ +static intmax_t seek_records = 0; + +/* Skip this many bytes in addition to 'seek_records' records before + output. */ +static intmax_t seek_bytes = 0; + +/* Whether the final output was done with a seek (rather than a write). */ +static bool final_op_was_seek; + +/* Copy only this many records. The default is effectively infinity. */ +static intmax_t max_records = INTMAX_MAX; + +/* Copy this many bytes in addition to 'max_records' records. */ +static idx_t max_bytes = 0; + +/* Bit vector of conversions to apply. */ +static int conversions_mask = 0; + +/* Open flags for the input and output files. */ +static int input_flags = 0; +static int output_flags = 0; + +/* Status flags for what is printed to stderr. */ +static int status_level = STATUS_DEFAULT; + +/* If nonzero, filter characters through the translation table. */ +static bool translation_needed = false; + +/* Number of partial blocks written. */ +static intmax_t w_partial = 0; + +/* Number of full blocks written. */ +static intmax_t w_full = 0; + +/* Number of partial blocks read. */ +static intmax_t r_partial = 0; + +/* Number of full blocks read. */ +static intmax_t r_full = 0; + +/* Number of bytes written. */ +static intmax_t w_bytes = 0; + +/* Last-reported number of bytes written, or negative if never reported. */ +static intmax_t reported_w_bytes = -1; + +/* Time that dd started. */ +static xtime_t start_time; + +/* Next time to report periodic progress. */ +static xtime_t next_time; + +/* If positive, the number of bytes output in the current progress line. */ +static int progress_len; + +/* True if input is seekable. */ +static bool input_seekable; + +/* Error number corresponding to initial attempt to lseek input. + If ESPIPE, do not issue any more diagnostics about it. */ +static int input_seek_errno; + +/* File offset of the input, in bytes, or -1 if it overflowed. */ +static off_t input_offset; + +/* True if a partial read should be diagnosed. */ +static bool warn_partial_read; + +/* Records truncated by conv=block. */ +static intmax_t r_truncate = 0; + +/* Output representation of newline and space characters. + They change if we're converting to EBCDIC. */ +static char newline_character = '\n'; +static char space_character = ' '; + +/* I/O buffers. */ +static char *ibuf; +static char *obuf; + +/* Current index into 'obuf'. */ +static idx_t oc = 0; + +/* Index into current line, for 'conv=block' and 'conv=unblock'. */ +static idx_t col = 0; + +/* The set of signals that are caught. */ +static sigset_t caught_signals; + +/* If nonzero, the value of the pending fatal signal. */ +static sig_atomic_t volatile interrupt_signal; + +/* A count of the number of pending info signals that have been received. */ +static sig_atomic_t volatile info_signal_count; + +/* Whether to discard cache for input or output. */ +static bool i_nocache, o_nocache; + +/* Whether to instruct the kernel to discard the complete file. */ +static bool i_nocache_eof, o_nocache_eof; + +/* Function used for read (to handle iflag=fullblock parameter). */ +static ssize_t (*iread_fnc) (int fd, char *buf, idx_t size); + +/* A longest symbol in the struct symbol_values tables below. */ +#define LONGEST_SYMBOL "count_bytes" + +/* A symbol and the corresponding integer value. */ +struct symbol_value +{ + char symbol[sizeof LONGEST_SYMBOL]; + int value; +}; + +/* Conversion symbols, for conv="...". */ +static struct symbol_value const conversions[] = +{ + {"ascii", C_ASCII | C_UNBLOCK | C_TWOBUFS}, /* EBCDIC to ASCII. */ + {"ebcdic", C_EBCDIC | C_BLOCK | C_TWOBUFS}, /* ASCII to EBCDIC. */ + {"ibm", C_IBM | C_BLOCK | C_TWOBUFS}, /* Different ASCII to EBCDIC. */ + {"block", C_BLOCK | C_TWOBUFS}, /* Variable to fixed length records. */ + {"unblock", C_UNBLOCK | C_TWOBUFS}, /* Fixed to variable length records. */ + {"lcase", C_LCASE | C_TWOBUFS}, /* Translate upper to lower case. */ + {"ucase", C_UCASE | C_TWOBUFS}, /* Translate lower to upper case. */ + {"sparse", C_SPARSE}, /* Try to sparsely write output. */ + {"swab", C_SWAB | C_TWOBUFS}, /* Swap bytes of input. */ + {"noerror", C_NOERROR}, /* Ignore i/o errors. */ + {"nocreat", C_NOCREAT}, /* Do not create output file. */ + {"excl", C_EXCL}, /* Fail if the output file already exists. */ + {"notrunc", C_NOTRUNC}, /* Do not truncate output file. */ + {"sync", C_SYNC}, /* Pad input records to ibs with NULs. */ + {"fdatasync", C_FDATASYNC}, /* Synchronize output data before finishing. */ + {"fsync", C_FSYNC}, /* Also synchronize output metadata. */ + {"", 0} +}; + +#define FFS_MASK(x) ((x) ^ ((x) & ((x) - 1))) +enum + { + /* Compute a value that's bitwise disjoint from the union + of all O_ values. */ + v = ~(0 + | O_APPEND + | O_BINARY + | O_CIO + | O_DIRECT + | O_DIRECTORY + | O_DSYNC + | O_NOATIME + | O_NOCTTY + | O_NOFOLLOW + | O_NOLINKS + | O_NONBLOCK + | O_SYNC + | O_TEXT + ), + + /* Use its lowest bits for private flags. */ + O_FULLBLOCK = FFS_MASK (v), + v2 = v ^ O_FULLBLOCK, + + O_NOCACHE = FFS_MASK (v2), + v3 = v2 ^ O_NOCACHE, + + O_COUNT_BYTES = FFS_MASK (v3), + v4 = v3 ^ O_COUNT_BYTES, + + O_SKIP_BYTES = FFS_MASK (v4), + v5 = v4 ^ O_SKIP_BYTES, + + O_SEEK_BYTES = FFS_MASK (v5) + }; + +/* Ensure that we got something. */ +verify (O_FULLBLOCK != 0); +verify (O_NOCACHE != 0); +verify (O_COUNT_BYTES != 0); +verify (O_SKIP_BYTES != 0); +verify (O_SEEK_BYTES != 0); + +#define MULTIPLE_BITS_SET(i) (((i) & ((i) - 1)) != 0) + +/* Ensure that this is a single-bit value. */ +verify ( ! MULTIPLE_BITS_SET (O_FULLBLOCK)); +verify ( ! MULTIPLE_BITS_SET (O_NOCACHE)); +verify ( ! MULTIPLE_BITS_SET (O_COUNT_BYTES)); +verify ( ! MULTIPLE_BITS_SET (O_SKIP_BYTES)); +verify ( ! MULTIPLE_BITS_SET (O_SEEK_BYTES)); + +/* Flags, for iflag="..." and oflag="...". */ +static struct symbol_value const flags[] = +{ + {"append", O_APPEND}, + {"binary", O_BINARY}, + {"cio", O_CIO}, + {"direct", O_DIRECT}, + {"directory", O_DIRECTORY}, + {"dsync", O_DSYNC}, + {"noatime", O_NOATIME}, + {"nocache", O_NOCACHE}, /* Discard cache. */ + {"noctty", O_NOCTTY}, + {"nofollow", HAVE_WORKING_O_NOFOLLOW ? O_NOFOLLOW : 0}, + {"nolinks", O_NOLINKS}, + {"nonblock", O_NONBLOCK}, + {"sync", O_SYNC}, + {"text", O_TEXT}, + {"fullblock", O_FULLBLOCK}, /* Accumulate full blocks from input. */ + {"count_bytes", O_COUNT_BYTES}, + {"skip_bytes", O_SKIP_BYTES}, + {"seek_bytes", O_SEEK_BYTES}, + {"", 0} +}; + +/* Status, for status="...". */ +static struct symbol_value const statuses[] = +{ + {"none", STATUS_NONE}, + {"noxfer", STATUS_NOXFER}, + {"progress", STATUS_PROGRESS}, + {"", 0} +}; + +/* Translation table formed by applying successive transformations. */ +static unsigned char trans_table[256]; + +/* Standard translation tables, taken from POSIX 1003.1-2013. + Beware of imitations; there are lots of ASCII<->EBCDIC tables + floating around the net, perhaps valid for some applications but + not correct here. */ + +static char const ascii_to_ebcdic[] = +{ + '\000', '\001', '\002', '\003', '\067', '\055', '\056', '\057', + '\026', '\005', '\045', '\013', '\014', '\015', '\016', '\017', + '\020', '\021', '\022', '\023', '\074', '\075', '\062', '\046', + '\030', '\031', '\077', '\047', '\034', '\035', '\036', '\037', + '\100', '\132', '\177', '\173', '\133', '\154', '\120', '\175', + '\115', '\135', '\134', '\116', '\153', '\140', '\113', '\141', + '\360', '\361', '\362', '\363', '\364', '\365', '\366', '\367', + '\370', '\371', '\172', '\136', '\114', '\176', '\156', '\157', + '\174', '\301', '\302', '\303', '\304', '\305', '\306', '\307', + '\310', '\311', '\321', '\322', '\323', '\324', '\325', '\326', + '\327', '\330', '\331', '\342', '\343', '\344', '\345', '\346', + '\347', '\350', '\351', '\255', '\340', '\275', '\232', '\155', + '\171', '\201', '\202', '\203', '\204', '\205', '\206', '\207', + '\210', '\211', '\221', '\222', '\223', '\224', '\225', '\226', + '\227', '\230', '\231', '\242', '\243', '\244', '\245', '\246', + '\247', '\250', '\251', '\300', '\117', '\320', '\137', '\007', + '\040', '\041', '\042', '\043', '\044', '\025', '\006', '\027', + '\050', '\051', '\052', '\053', '\054', '\011', '\012', '\033', + '\060', '\061', '\032', '\063', '\064', '\065', '\066', '\010', + '\070', '\071', '\072', '\073', '\004', '\024', '\076', '\341', + '\101', '\102', '\103', '\104', '\105', '\106', '\107', '\110', + '\111', '\121', '\122', '\123', '\124', '\125', '\126', '\127', + '\130', '\131', '\142', '\143', '\144', '\145', '\146', '\147', + '\150', '\151', '\160', '\161', '\162', '\163', '\164', '\165', + '\166', '\167', '\170', '\200', '\212', '\213', '\214', '\215', + '\216', '\217', '\220', '\152', '\233', '\234', '\235', '\236', + '\237', '\240', '\252', '\253', '\254', '\112', '\256', '\257', + '\260', '\261', '\262', '\263', '\264', '\265', '\266', '\267', + '\270', '\271', '\272', '\273', '\274', '\241', '\276', '\277', + '\312', '\313', '\314', '\315', '\316', '\317', '\332', '\333', + '\334', '\335', '\336', '\337', '\352', '\353', '\354', '\355', + '\356', '\357', '\372', '\373', '\374', '\375', '\376', '\377' +}; + +static char const ascii_to_ibm[] = +{ + '\000', '\001', '\002', '\003', '\067', '\055', '\056', '\057', + '\026', '\005', '\045', '\013', '\014', '\015', '\016', '\017', + '\020', '\021', '\022', '\023', '\074', '\075', '\062', '\046', + '\030', '\031', '\077', '\047', '\034', '\035', '\036', '\037', + '\100', '\132', '\177', '\173', '\133', '\154', '\120', '\175', + '\115', '\135', '\134', '\116', '\153', '\140', '\113', '\141', + '\360', '\361', '\362', '\363', '\364', '\365', '\366', '\367', + '\370', '\371', '\172', '\136', '\114', '\176', '\156', '\157', + '\174', '\301', '\302', '\303', '\304', '\305', '\306', '\307', + '\310', '\311', '\321', '\322', '\323', '\324', '\325', '\326', + '\327', '\330', '\331', '\342', '\343', '\344', '\345', '\346', + '\347', '\350', '\351', '\255', '\340', '\275', '\137', '\155', + '\171', '\201', '\202', '\203', '\204', '\205', '\206', '\207', + '\210', '\211', '\221', '\222', '\223', '\224', '\225', '\226', + '\227', '\230', '\231', '\242', '\243', '\244', '\245', '\246', + '\247', '\250', '\251', '\300', '\117', '\320', '\241', '\007', + '\040', '\041', '\042', '\043', '\044', '\025', '\006', '\027', + '\050', '\051', '\052', '\053', '\054', '\011', '\012', '\033', + '\060', '\061', '\032', '\063', '\064', '\065', '\066', '\010', + '\070', '\071', '\072', '\073', '\004', '\024', '\076', '\341', + '\101', '\102', '\103', '\104', '\105', '\106', '\107', '\110', + '\111', '\121', '\122', '\123', '\124', '\125', '\126', '\127', + '\130', '\131', '\142', '\143', '\144', '\145', '\146', '\147', + '\150', '\151', '\160', '\161', '\162', '\163', '\164', '\165', + '\166', '\167', '\170', '\200', '\212', '\213', '\214', '\215', + '\216', '\217', '\220', '\232', '\233', '\234', '\235', '\236', + '\237', '\240', '\252', '\253', '\254', '\255', '\256', '\257', + '\260', '\261', '\262', '\263', '\264', '\265', '\266', '\267', + '\270', '\271', '\272', '\273', '\274', '\275', '\276', '\277', + '\312', '\313', '\314', '\315', '\316', '\317', '\332', '\333', + '\334', '\335', '\336', '\337', '\352', '\353', '\354', '\355', + '\356', '\357', '\372', '\373', '\374', '\375', '\376', '\377' +}; + +static char const ebcdic_to_ascii[] = +{ + '\000', '\001', '\002', '\003', '\234', '\011', '\206', '\177', + '\227', '\215', '\216', '\013', '\014', '\015', '\016', '\017', + '\020', '\021', '\022', '\023', '\235', '\205', '\010', '\207', + '\030', '\031', '\222', '\217', '\034', '\035', '\036', '\037', + '\200', '\201', '\202', '\203', '\204', '\012', '\027', '\033', + '\210', '\211', '\212', '\213', '\214', '\005', '\006', '\007', + '\220', '\221', '\026', '\223', '\224', '\225', '\226', '\004', + '\230', '\231', '\232', '\233', '\024', '\025', '\236', '\032', + '\040', '\240', '\241', '\242', '\243', '\244', '\245', '\246', + '\247', '\250', '\325', '\056', '\074', '\050', '\053', '\174', + '\046', '\251', '\252', '\253', '\254', '\255', '\256', '\257', + '\260', '\261', '\041', '\044', '\052', '\051', '\073', '\176', + '\055', '\057', '\262', '\263', '\264', '\265', '\266', '\267', + '\270', '\271', '\313', '\054', '\045', '\137', '\076', '\077', + '\272', '\273', '\274', '\275', '\276', '\277', '\300', '\301', + '\302', '\140', '\072', '\043', '\100', '\047', '\075', '\042', + '\303', '\141', '\142', '\143', '\144', '\145', '\146', '\147', + '\150', '\151', '\304', '\305', '\306', '\307', '\310', '\311', + '\312', '\152', '\153', '\154', '\155', '\156', '\157', '\160', + '\161', '\162', '\136', '\314', '\315', '\316', '\317', '\320', + '\321', '\345', '\163', '\164', '\165', '\166', '\167', '\170', + '\171', '\172', '\322', '\323', '\324', '\133', '\326', '\327', + '\330', '\331', '\332', '\333', '\334', '\335', '\336', '\337', + '\340', '\341', '\342', '\343', '\344', '\135', '\346', '\347', + '\173', '\101', '\102', '\103', '\104', '\105', '\106', '\107', + '\110', '\111', '\350', '\351', '\352', '\353', '\354', '\355', + '\175', '\112', '\113', '\114', '\115', '\116', '\117', '\120', + '\121', '\122', '\356', '\357', '\360', '\361', '\362', '\363', + '\134', '\237', '\123', '\124', '\125', '\126', '\127', '\130', + '\131', '\132', '\364', '\365', '\366', '\367', '\370', '\371', + '\060', '\061', '\062', '\063', '\064', '\065', '\066', '\067', + '\070', '\071', '\372', '\373', '\374', '\375', '\376', '\377' +}; + +/* True if we need to close the standard output *stream*. */ +static bool close_stdout_required = true; + +/* The only reason to close the standard output *stream* is if + parse_long_options fails (as it does for --help or --version). + In any other case, dd uses only the STDOUT_FILENO file descriptor, + and the "cleanup" function calls "close (STDOUT_FILENO)". + Closing the file descriptor and then letting the usual atexit-run + close_stdout function call "fclose (stdout)" would result in a + harmless failure of the close syscall (with errno EBADF). + This function serves solely to avoid the unnecessary close_stdout + call, once parse_long_options has succeeded. + Meanwhile, we guarantee that the standard error stream is flushed, + by inlining the last half of close_stdout as needed. */ +static void +maybe_close_stdout (void) +{ + if (close_stdout_required) + close_stdout (); + else if (close_stream (stderr) != 0) + _exit (EXIT_FAILURE); +} + +/* Like the 'error' function but handle any pending newline. */ + +ATTRIBUTE_FORMAT ((__printf__, 3, 4)) +static void +nl_error (int status, int errnum, char const *fmt, ...) +{ + if (0 < progress_len) + { + fputc ('\n', stderr); + progress_len = 0; + } + + va_list ap; + va_start (ap, fmt); + verror (status, errnum, fmt, ap); + va_end (ap); +} + +#define error nl_error + +void +usage (int status) +{ + if (status != EXIT_SUCCESS) + emit_try_help (); + else + { + printf (_("\ +Usage: %s [OPERAND]...\n\ + or: %s OPTION\n\ +"), + program_name, program_name); + fputs (_("\ +Copy a file, converting and formatting according to the operands.\n\ +\n\ + bs=BYTES read and write up to BYTES bytes at a time (default: 512);\n\ + overrides ibs and obs\n\ + cbs=BYTES convert BYTES bytes at a time\n\ + conv=CONVS convert the file as per the comma separated symbol list\n\ + count=N copy only N input blocks\n\ + ibs=BYTES read up to BYTES bytes at a time (default: 512)\n\ +"), stdout); + fputs (_("\ + if=FILE read from FILE instead of stdin\n\ + iflag=FLAGS read as per the comma separated symbol list\n\ + obs=BYTES write BYTES bytes at a time (default: 512)\n\ + of=FILE write to FILE instead of stdout\n\ + oflag=FLAGS write as per the comma separated symbol list\n\ + seek=N (or oseek=N) skip N obs-sized output blocks\n\ + skip=N (or iseek=N) skip N ibs-sized input blocks\n\ + status=LEVEL The LEVEL of information to print to stderr;\n\ + 'none' suppresses everything but error messages,\n\ + 'noxfer' suppresses the final transfer statistics,\n\ + 'progress' shows periodic transfer statistics\n\ +"), stdout); + fputs (_("\ +\n\ +N and BYTES may be followed by the following multiplicative suffixes:\n\ +c=1, w=2, b=512, kB=1000, K=1024, MB=1000*1000, M=1024*1024, xM=M,\n\ +GB=1000*1000*1000, G=1024*1024*1024, and so on for T, P, E, Z, Y.\n\ +Binary prefixes can be used, too: KiB=K, MiB=M, and so on.\n\ +If N ends in 'B', it counts bytes not blocks.\n\ +\n\ +Each CONV symbol may be:\n\ +\n\ +"), stdout); + fputs (_("\ + ascii from EBCDIC to ASCII\n\ + ebcdic from ASCII to EBCDIC\n\ + ibm from ASCII to alternate EBCDIC\n\ + block pad newline-terminated records with spaces to cbs-size\n\ + unblock replace trailing spaces in cbs-size records with newline\n\ + lcase change upper case to lower case\n\ + ucase change lower case to upper case\n\ + sparse try to seek rather than write all-NUL output blocks\n\ + swab swap every pair of input bytes\n\ + sync pad every input block with NULs to ibs-size; when used\n\ + with block or unblock, pad with spaces rather than NULs\n\ +"), stdout); + fputs (_("\ + excl fail if the output file already exists\n\ + nocreat do not create the output file\n\ + notrunc do not truncate the output file\n\ + noerror continue after read errors\n\ + fdatasync physically write output file data before finishing\n\ + fsync likewise, but also write metadata\n\ +"), stdout); + fputs (_("\ +\n\ +Each FLAG symbol may be:\n\ +\n\ + append append mode (makes sense only for output; conv=notrunc suggested)\n\ +"), stdout); + if (O_CIO) + fputs (_(" cio use concurrent I/O for data\n"), stdout); + if (O_DIRECT) + fputs (_(" direct use direct I/O for data\n"), stdout); + if (O_DIRECTORY) + fputs (_(" directory fail unless a directory\n"), stdout); + if (O_DSYNC) + fputs (_(" dsync use synchronized I/O for data\n"), stdout); + if (O_SYNC) + fputs (_(" sync likewise, but also for metadata\n"), stdout); + fputs (_(" fullblock accumulate full blocks of input (iflag only)\n"), + stdout); + if (O_NONBLOCK) + fputs (_(" nonblock use non-blocking I/O\n"), stdout); + if (O_NOATIME) + fputs (_(" noatime do not update access time\n"), stdout); +#if HAVE_POSIX_FADVISE + if (O_NOCACHE) + fputs (_(" nocache Request to drop cache. See also oflag=sync\n"), + stdout); +#endif + if (O_NOCTTY) + fputs (_(" noctty do not assign controlling terminal from file\n"), + stdout); + if (HAVE_WORKING_O_NOFOLLOW) + fputs (_(" nofollow do not follow symlinks\n"), stdout); + if (O_NOLINKS) + fputs (_(" nolinks fail if multiply-linked\n"), stdout); + if (O_BINARY) + fputs (_(" binary use binary I/O for data\n"), stdout); + if (O_TEXT) + fputs (_(" text use text I/O for data\n"), stdout); + + { + printf (_("\ +\n\ +Sending a %s signal to a running 'dd' process makes it\n\ +print I/O statistics to standard error and then resume copying.\n\ +\n\ +Options are:\n\ +\n\ +"), SIGINFO == SIGUSR1 ? "USR1" : "INFO"); + } + + fputs (HELP_OPTION_DESCRIPTION, stdout); + fputs (VERSION_OPTION_DESCRIPTION, stdout); + emit_ancillary_info (PROGRAM_NAME); + } + exit (status); +} + +/* Common options to use when displaying sizes and rates. */ + +enum { human_opts = (human_autoscale | human_round_to_nearest + | human_space_before_unit | human_SI | human_B) }; + +/* Ensure input buffer IBUF is allocated. */ + +static void +alloc_ibuf (void) +{ + if (ibuf) + return; + + bool extra_byte_for_swab = !!(conversions_mask & C_SWAB); + ibuf = alignalloc (page_size, input_blocksize + extra_byte_for_swab); + if (!ibuf) + { + char hbuf[LONGEST_HUMAN_READABLE + 1]; + die (EXIT_FAILURE, 0, + _("memory exhausted by input buffer of size %td bytes (%s)"), + input_blocksize, + human_readable (input_blocksize, hbuf, + human_opts | human_base_1024, 1, 1)); + } +} + +/* Ensure output buffer OBUF is allocated/initialized. */ + +static void +alloc_obuf (void) +{ + if (obuf) + return; + + if (conversions_mask & C_TWOBUFS) + { + obuf = alignalloc (page_size, output_blocksize); + if (!obuf) + { + char hbuf[LONGEST_HUMAN_READABLE + 1]; + die (EXIT_FAILURE, 0, + _("memory exhausted by output buffer of size %td" + " bytes (%s)"), + output_blocksize, + human_readable (output_blocksize, hbuf, + human_opts | human_base_1024, 1, 1)); + } + } + else + { + alloc_ibuf (); + obuf = ibuf; + } +} + +static void +translate_charset (char const *new_trans) +{ + for (int i = 0; i < 256; i++) + trans_table[i] = new_trans[trans_table[i]]; + translation_needed = true; +} + +/* Return true if I has more than one bit set. I must be nonnegative. */ + +static inline bool +multiple_bits_set (int i) +{ + return MULTIPLE_BITS_SET (i); +} + +static bool +abbreviation_lacks_prefix (char const *message) +{ + return message[strlen (message) - 2] == ' '; +} + +/* Print transfer statistics. */ + +static void +print_xfer_stats (xtime_t progress_time) +{ + xtime_t now = progress_time ? progress_time : gethrxtime (); + static char const slash_s[] = "/s"; + char hbuf[3][LONGEST_HUMAN_READABLE + sizeof slash_s]; + double delta_s; + char const *bytes_per_second; + char const *si = human_readable (w_bytes, hbuf[0], human_opts, 1, 1); + char const *iec = human_readable (w_bytes, hbuf[1], + human_opts | human_base_1024, 1, 1); + + /* Use integer arithmetic to compute the transfer rate, + since that makes it easy to use SI abbreviations. */ + char *bpsbuf = hbuf[2]; + int bpsbufsize = sizeof hbuf[2]; + if (start_time < now) + { + double XTIME_PRECISIONe0 = XTIME_PRECISION; + xtime_t delta_xtime = now - start_time; + delta_s = delta_xtime / XTIME_PRECISIONe0; + bytes_per_second = human_readable (w_bytes, bpsbuf, human_opts, + XTIME_PRECISION, delta_xtime); + strcat (bytes_per_second - bpsbuf + bpsbuf, slash_s); + } + else + { + delta_s = 0; + snprintf (bpsbuf, bpsbufsize, "%s B/s", _("Infinity")); + bytes_per_second = bpsbuf; + } + + if (progress_time) + fputc ('\r', stderr); + + /* Use full seconds when printing progress, since the progress + report is output once per second and there is little point + displaying any subsecond jitter. Use default precision with %g + otherwise, as this provides more-useful output then. With long + transfers %g can generate a number with an exponent; that is OK. */ + char delta_s_buf[24]; + snprintf (delta_s_buf, sizeof delta_s_buf, + progress_time ? "%.0f s" : "%g s", delta_s); + + int stats_len + = (abbreviation_lacks_prefix (si) + ? fprintf (stderr, + ngettext ("%"PRIdMAX" byte copied, %s, %s", + "%"PRIdMAX" bytes copied, %s, %s", + select_plural (w_bytes)), + w_bytes, delta_s_buf, bytes_per_second) + : abbreviation_lacks_prefix (iec) + ? fprintf (stderr, + _("%"PRIdMAX" bytes (%s) copied, %s, %s"), + w_bytes, si, delta_s_buf, bytes_per_second) + : fprintf (stderr, + _("%"PRIdMAX" bytes (%s, %s) copied, %s, %s"), + w_bytes, si, iec, delta_s_buf, bytes_per_second)); + + if (progress_time) + { + /* Erase any trailing junk on the output line by outputting + spaces. In theory this could glitch the display because the + formatted translation of a line describing a larger file + could consume fewer screen columns than the strlen difference + from the previously formatted translation. In practice this + does not seem to be a problem. */ + if (0 <= stats_len && stats_len < progress_len) + fprintf (stderr, "%*s", progress_len - stats_len, ""); + progress_len = stats_len; + } + else + fputc ('\n', stderr); + + reported_w_bytes = w_bytes; +} + +static void +print_stats (void) +{ + if (status_level == STATUS_NONE) + return; + + if (0 < progress_len) + { + fputc ('\n', stderr); + progress_len = 0; + } + + fprintf (stderr, + _("%"PRIdMAX"+%"PRIdMAX" records in\n" + "%"PRIdMAX"+%"PRIdMAX" records out\n"), + r_full, r_partial, w_full, w_partial); + + if (r_truncate != 0) + fprintf (stderr, + ngettext ("%"PRIdMAX" truncated record\n", + "%"PRIdMAX" truncated records\n", + select_plural (r_truncate)), + r_truncate); + + if (status_level == STATUS_NOXFER) + return; + + print_xfer_stats (0); +} + +/* An ordinary signal was received; arrange for the program to exit. */ + +static void +interrupt_handler (int sig) +{ + if (! SA_RESETHAND) + signal (sig, SIG_DFL); + interrupt_signal = sig; +} + +/* An info signal was received; arrange for the program to print status. */ + +static void +siginfo_handler (int sig) +{ + if (! SA_NOCLDSTOP) + signal (sig, siginfo_handler); + info_signal_count++; +} + +/* Install the signal handlers. */ + +static void +install_signal_handlers (void) +{ + bool catch_siginfo = ! (SIGINFO == SIGUSR1 && getenv ("POSIXLY_CORRECT")); + +#if SA_NOCLDSTOP + + struct sigaction act; + sigemptyset (&caught_signals); + if (catch_siginfo) + sigaddset (&caught_signals, SIGINFO); + sigaction (SIGINT, NULL, &act); + if (act.sa_handler != SIG_IGN) + sigaddset (&caught_signals, SIGINT); + act.sa_mask = caught_signals; + + if (sigismember (&caught_signals, SIGINFO)) + { + act.sa_handler = siginfo_handler; + /* Note we don't use SA_RESTART here and instead + handle EINTR explicitly in iftruncate etc. + to avoid blocking on noncommitted read/write calls. */ + act.sa_flags = 0; + sigaction (SIGINFO, &act, NULL); + } + + if (sigismember (&caught_signals, SIGINT)) + { + act.sa_handler = interrupt_handler; + act.sa_flags = SA_NODEFER | SA_RESETHAND; + sigaction (SIGINT, &act, NULL); + } + +#else + + if (catch_siginfo) + { + signal (SIGINFO, siginfo_handler); + siginterrupt (SIGINFO, 1); + } + if (signal (SIGINT, SIG_IGN) != SIG_IGN) + { + signal (SIGINT, interrupt_handler); + siginterrupt (SIGINT, 1); + } +#endif +} + +/* Close FD. Return 0 if successful, -1 (setting errno) otherwise. + If close fails with errno == EINTR, POSIX says the file descriptor + is in an unspecified state, so keep trying to close FD but do not + consider EBADF to be an error. Do not process signals. This all + differs somewhat from functions like ifdatasync and ifsync. */ +static int +iclose (int fd) +{ + if (close (fd) != 0) + do + if (errno != EINTR) + return -1; + while (close (fd) != 0 && errno != EBADF); + + return 0; +} + +static int synchronize_output (void); + +static void +cleanup (void) +{ + if (!interrupt_signal) + { + int sync_status = synchronize_output (); + if (sync_status) + exit (sync_status); + } + + if (iclose (STDIN_FILENO) != 0) + die (EXIT_FAILURE, errno, _("closing input file %s"), quoteaf (input_file)); + + /* Don't remove this call to close, even though close_stdout + closes standard output. This close is necessary when cleanup + is called as a consequence of signal handling. */ + if (iclose (STDOUT_FILENO) != 0) + die (EXIT_FAILURE, errno, + _("closing output file %s"), quoteaf (output_file)); +} + +/* Process any pending signals. If signals are caught, this function + should be called periodically. Ideally there should never be an + unbounded amount of time when signals are not being processed. */ + +static void +process_signals (void) +{ + while (interrupt_signal || info_signal_count) + { + int interrupt; + int infos; + sigset_t oldset; + + sigprocmask (SIG_BLOCK, &caught_signals, &oldset); + + /* Reload interrupt_signal and info_signal_count, in case a new + signal was handled before sigprocmask took effect. */ + interrupt = interrupt_signal; + infos = info_signal_count; + + if (infos) + info_signal_count = infos - 1; + + sigprocmask (SIG_SETMASK, &oldset, NULL); + + if (interrupt) + cleanup (); + print_stats (); + if (interrupt) + raise (interrupt); + } +} + +static void +finish_up (void) +{ + /* Process signals first, so that cleanup is called at most once. */ + process_signals (); + cleanup (); + print_stats (); +} + +static void +quit (int code) +{ + finish_up (); + exit (code); +} + +/* Return LEN rounded down to a multiple of IO_BUFSIZE + (to minimize calls to the expensive posix_fadvise (,POSIX_FADV_DONTNEED), + while storing the remainder internally per FD. + Pass LEN == 0 to get the current remainder. */ + +static off_t +cache_round (int fd, off_t len) +{ + static off_t i_pending, o_pending; + off_t *pending = (fd == STDIN_FILENO ? &i_pending : &o_pending); + + if (len) + { + intmax_t c_pending; + if (INT_ADD_WRAPV (*pending, len, &c_pending)) + c_pending = INTMAX_MAX; + *pending = c_pending % IO_BUFSIZE; + if (c_pending > *pending) + len = c_pending - *pending; + else + len = 0; + } + else + len = *pending; + + return len; +} + +/* Discard the cache from the current offset of either + STDIN_FILENO or STDOUT_FILENO. + Return true on success. */ + +static bool +invalidate_cache (int fd, off_t len) +{ + int adv_ret = -1; + off_t offset; + bool nocache_eof = (fd == STDIN_FILENO ? i_nocache_eof : o_nocache_eof); + + /* Minimize syscalls. */ + off_t clen = cache_round (fd, len); + if (len && !clen) + return true; /* Don't advise this time. */ + else if (! len && ! clen && ! nocache_eof) + return true; + off_t pending = len ? cache_round (fd, 0) : 0; + + if (fd == STDIN_FILENO) + { + if (input_seekable) + offset = input_offset; + else + { + offset = -1; + errno = ESPIPE; + } + } + else + { + static off_t output_offset = -2; + + if (output_offset != -1) + { + if (output_offset < 0) + output_offset = lseek (fd, 0, SEEK_CUR); + else if (len) + output_offset += clen + pending; + } + + offset = output_offset; + } + + if (0 <= offset) + { + if (! len && clen && nocache_eof) + { + pending = clen; + clen = 0; + } + + /* Note we're being careful here to only invalidate what + we've read, so as not to dump any read ahead cache. + Note also the kernel is conservative and only invalidates + full pages in the specified range. */ +#if HAVE_POSIX_FADVISE + offset = offset - clen - pending; + /* ensure full page specified when invalidating to eof. */ + if (clen == 0) + offset -= offset % page_size; + adv_ret = posix_fadvise (fd, offset, clen, POSIX_FADV_DONTNEED); +#else + errno = ENOTSUP; +#endif + } + + return adv_ret != -1 ? true : false; +} + +/* Read from FD into the buffer BUF of size SIZE, processing any + signals that arrive before bytes are read. Return the number of + bytes read if successful, -1 (setting errno) on failure. */ + +static ssize_t +iread (int fd, char *buf, idx_t size) +{ + ssize_t nread; + static ssize_t prev_nread; + + do + { + process_signals (); + nread = read (fd, buf, size); + /* Ignore final read error with iflag=direct as that + returns EINVAL due to the non aligned file offset. */ + if (nread == -1 && errno == EINVAL + && 0 < prev_nread && prev_nread < size + && (input_flags & O_DIRECT)) + { + errno = 0; + nread = 0; + } + } + while (nread < 0 && errno == EINTR); + + /* Short read may be due to received signal. */ + if (0 < nread && nread < size) + process_signals (); + + if (0 < nread && warn_partial_read) + { + if (0 < prev_nread && prev_nread < size) + { + idx_t prev = prev_nread; + if (status_level != STATUS_NONE) + error (0, 0, ngettext (("warning: partial read (%td byte); " + "suggest iflag=fullblock"), + ("warning: partial read (%td bytes); " + "suggest iflag=fullblock"), + select_plural (prev)), + prev); + warn_partial_read = false; + } + } + + prev_nread = nread; + return nread; +} + +/* Wrapper around iread function to accumulate full blocks. */ +static ssize_t +iread_fullblock (int fd, char *buf, idx_t size) +{ + ssize_t nread = 0; + + while (0 < size) + { + ssize_t ncurr = iread (fd, buf, size); + if (ncurr < 0) + return ncurr; + if (ncurr == 0) + break; + nread += ncurr; + buf += ncurr; + size -= ncurr; + } + + return nread; +} + +/* Write to FD the buffer BUF of size SIZE, processing any signals + that arrive. Return the number of bytes written, setting errno if + this is less than SIZE. Keep trying if there are partial + writes. */ + +static idx_t +iwrite (int fd, char const *buf, idx_t size) +{ + idx_t total_written = 0; + + if ((output_flags & O_DIRECT) && size < output_blocksize) + { + int old_flags = fcntl (STDOUT_FILENO, F_GETFL); + if (fcntl (STDOUT_FILENO, F_SETFL, old_flags & ~O_DIRECT) != 0 + && status_level != STATUS_NONE) + error (0, errno, _("failed to turn off O_DIRECT: %s"), + quotef (output_file)); + + /* Since we have just turned off O_DIRECT for the final write, + we try to preserve some of its semantics. */ + + /* Call invalidate_cache to setup the appropriate offsets + for subsequent calls. */ + o_nocache_eof = true; + invalidate_cache (STDOUT_FILENO, 0); + + /* Attempt to ensure that that final block is committed + to stable storage as quickly as possible. */ + conversions_mask |= C_FSYNC; + + /* After the subsequent fsync we'll call invalidate_cache + to attempt to clear all data from the page cache. */ + } + + while (total_written < size) + { + ssize_t nwritten = 0; + process_signals (); + + /* Perform a seek for a NUL block if sparse output is enabled. */ + final_op_was_seek = false; + if ((conversions_mask & C_SPARSE) && is_nul (buf, size)) + { + if (lseek (fd, size, SEEK_CUR) < 0) + { + conversions_mask &= ~C_SPARSE; + /* Don't warn about the advisory sparse request. */ + } + else + { + final_op_was_seek = true; + nwritten = size; + } + } + + if (!nwritten) + nwritten = write (fd, buf + total_written, size - total_written); + + if (nwritten < 0) + { + if (errno != EINTR) + break; + } + else if (nwritten == 0) + { + /* Some buggy drivers return 0 when one tries to write beyond + a device's end. (Example: Linux kernel 1.2.13 on /dev/fd0.) + Set errno to ENOSPC so they get a sensible diagnostic. */ + errno = ENOSPC; + break; + } + else + total_written += nwritten; + } + + if (o_nocache && total_written) + invalidate_cache (fd, total_written); + + return total_written; +} + +/* Write, then empty, the output buffer 'obuf'. */ + +static void +write_output (void) +{ + idx_t nwritten = iwrite (STDOUT_FILENO, obuf, output_blocksize); + w_bytes += nwritten; + if (nwritten != output_blocksize) + { + error (0, errno, _("writing to %s"), quoteaf (output_file)); + if (nwritten != 0) + w_partial++; + quit (EXIT_FAILURE); + } + else + w_full++; + oc = 0; +} + +/* Restart on EINTR from fdatasync. */ + +static int +ifdatasync (int fd) +{ + int ret; + + do + { + process_signals (); + ret = fdatasync (fd); + } + while (ret < 0 && errno == EINTR); + + return ret; +} + +/* Restart on EINTR from fd_reopen. */ + +static int +ifd_reopen (int desired_fd, char const *file, int flag, mode_t mode) +{ + int ret; + + do + { + process_signals (); + ret = fd_reopen (desired_fd, file, flag, mode); + } + while (ret < 0 && errno == EINTR); + + return ret; +} + +/* Restart on EINTR from fstat. */ + +static int +ifstat (int fd, struct stat *st) +{ + int ret; + + do + { + process_signals (); + ret = fstat (fd, st); + } + while (ret < 0 && errno == EINTR); + + return ret; +} + +/* Restart on EINTR from fsync. */ + +static int +ifsync (int fd) +{ + int ret; + + do + { + process_signals (); + ret = fsync (fd); + } + while (ret < 0 && errno == EINTR); + + return ret; +} + +/* Restart on EINTR from ftruncate. */ + +static int +iftruncate (int fd, off_t length) +{ + int ret; + + do + { + process_signals (); + ret = ftruncate (fd, length); + } + while (ret < 0 && errno == EINTR); + + return ret; +} + +/* Return true if STR is of the form "PATTERN" or "PATTERNDELIM...". */ + +ATTRIBUTE_PURE +static bool +operand_matches (char const *str, char const *pattern, char delim) +{ + while (*pattern) + if (*str++ != *pattern++) + return false; + return !*str || *str == delim; +} + +/* Interpret one "conv=..." or similar operand STR according to the + symbols in TABLE, returning the flags specified. If the operand + cannot be parsed, use ERROR_MSGID to generate a diagnostic. */ + +static int +parse_symbols (char const *str, struct symbol_value const *table, + bool exclusive, char const *error_msgid) +{ + int value = 0; + + while (true) + { + char const *strcomma = strchr (str, ','); + struct symbol_value const *entry; + + for (entry = table; + ! (operand_matches (str, entry->symbol, ',') && entry->value); + entry++) + { + if (! entry->symbol[0]) + { + idx_t slen = strcomma ? strcomma - str : strlen (str); + error (0, 0, "%s: %s", _(error_msgid), + quotearg_n_style_mem (0, locale_quoting_style, str, slen)); + usage (EXIT_FAILURE); + } + } + + if (exclusive) + value = entry->value; + else + value |= entry->value; + if (!strcomma) + break; + str = strcomma + 1; + } + + return value; +} + +/* Return the value of STR, interpreted as a non-negative decimal integer, + optionally multiplied by various values. + Set *INVALID to an appropriate error value and return INTMAX_MAX if + it is an overflow, an indeterminate value if some other error occurred. */ + +static intmax_t +parse_integer (char const *str, strtol_error *invalid) +{ + /* Call xstrtoumax, not xstrtoimax, since we don't want to + allow strings like " -0". Initialize N to an interminate value; + calling code should not rely on this function returning 0 + when *INVALID represents a non-overflow error. */ + int indeterminate = 0; + uintmax_t n = indeterminate; + char *suffix; + static char const suffixes[] = "bcEGkKMPTwYZ0"; + strtol_error e = xstrtoumax (str, &suffix, 10, &n, suffixes); + intmax_t result; + + if ((e & ~LONGINT_OVERFLOW) == LONGINT_INVALID_SUFFIX_CHAR + && suffix[-1] != 'B' && *suffix == 'B') + { + suffix++; + if (!*suffix) + e &= ~LONGINT_INVALID_SUFFIX_CHAR; + } + + if ((e & ~LONGINT_OVERFLOW) == LONGINT_INVALID_SUFFIX_CHAR + && *suffix == 'x' && ! (suffix[-1] == 'B' && strchr (suffix + 1, 'B'))) + { + uintmax_t o; + strtol_error f = xstrtoumax (suffix + 1, &suffix, 10, &o, suffixes); + if ((f & ~LONGINT_OVERFLOW) != LONGINT_OK) + { + e = f; + result = indeterminate; + } + else if (INT_MULTIPLY_WRAPV (n, o, &result) + || (result != 0 && ((e | f) & LONGINT_OVERFLOW))) + { + e = LONGINT_OVERFLOW; + result = INTMAX_MAX; + } + else + { + if (result == 0 && STRPREFIX (str, "0x")) + error (0, 0, + _("warning: %s is a zero multiplier; " + "use %s if that is intended"), + quote_n (0, "0x"), quote_n (1, "00x")); + e = LONGINT_OK; + } + } + else if (n <= INTMAX_MAX) + result = n; + else + { + e = LONGINT_OVERFLOW; + result = INTMAX_MAX; + } + + *invalid = e; + return result; +} + +/* OPERAND is of the form "X=...". Return true if X is NAME. */ + +ATTRIBUTE_PURE +static bool +operand_is (char const *operand, char const *name) +{ + return operand_matches (operand, name, '='); +} + +static void +scanargs (int argc, char *const *argv) +{ + idx_t blocksize = 0; + intmax_t count = INTMAX_MAX; + intmax_t skip = 0; + intmax_t seek = 0; + bool count_B = false, skip_B = false, seek_B = false; + + for (int i = optind; i < argc; i++) + { + char const *name = argv[i]; + char const *val = strchr (name, '='); + + if (val == NULL) + { + error (0, 0, _("unrecognized operand %s"), + quote (name)); + usage (EXIT_FAILURE); + } + val++; + + if (operand_is (name, "if")) + input_file = val; + else if (operand_is (name, "of")) + output_file = val; + else if (operand_is (name, "conv")) + conversions_mask |= parse_symbols (val, conversions, false, + N_("invalid conversion")); + else if (operand_is (name, "iflag")) + input_flags |= parse_symbols (val, flags, false, + N_("invalid input flag")); + else if (operand_is (name, "oflag")) + output_flags |= parse_symbols (val, flags, false, + N_("invalid output flag")); + else if (operand_is (name, "status")) + status_level = parse_symbols (val, statuses, true, + N_("invalid status level")); + else + { + strtol_error invalid = LONGINT_OK; + intmax_t n = parse_integer (val, &invalid); + bool has_B = !!strchr (val, 'B'); + intmax_t n_min = 0; + intmax_t n_max = INTMAX_MAX; + idx_t *converted_idx = NULL; + + /* Maximum blocksize. Keep it smaller than IDX_MAX, so that + it fits into blocksize vars even if 1 is added for conv=swab. + Do not exceed SSIZE_MAX, for the benefit of system calls + like "read". And do not exceed OFF_T_MAX, for the + benefit of the large-offset seek code. */ + idx_t max_blocksize = MIN (IDX_MAX - 1, MIN (SSIZE_MAX, OFF_T_MAX)); + + if (operand_is (name, "ibs")) + { + n_min = 1; + n_max = max_blocksize; + converted_idx = &input_blocksize; + } + else if (operand_is (name, "obs")) + { + n_min = 1; + n_max = max_blocksize; + converted_idx = &output_blocksize; + } + else if (operand_is (name, "bs")) + { + n_min = 1; + n_max = max_blocksize; + converted_idx = &blocksize; + } + else if (operand_is (name, "cbs")) + { + n_min = 1; + n_max = MIN (SIZE_MAX, IDX_MAX); + converted_idx = &conversion_blocksize; + } + else if (operand_is (name, "skip") || operand_is (name, "iseek")) + { + skip = n; + skip_B = has_B; + } + else if (operand_is (name + (*name == 'o'), "seek")) + { + seek = n; + seek_B = has_B; + } + else if (operand_is (name, "count")) + { + count = n; + count_B = has_B; + } + else + { + error (0, 0, _("unrecognized operand %s"), + quote (name)); + usage (EXIT_FAILURE); + } + + if (n < n_min) + invalid = LONGINT_INVALID; + else if (n_max < n) + invalid = LONGINT_OVERFLOW; + + if (invalid != LONGINT_OK) + die (EXIT_FAILURE, invalid == LONGINT_OVERFLOW ? EOVERFLOW : 0, + "%s: %s", _("invalid number"), quote (val)); + else if (converted_idx) + *converted_idx = n; + } + } + + if (blocksize) + input_blocksize = output_blocksize = blocksize; + else + { + /* POSIX says dd aggregates partial reads into + output_blocksize if bs= is not specified. */ + conversions_mask |= C_TWOBUFS; + } + + if (input_blocksize == 0) + input_blocksize = DEFAULT_BLOCKSIZE; + if (output_blocksize == 0) + output_blocksize = DEFAULT_BLOCKSIZE; + if (conversion_blocksize == 0) + conversions_mask &= ~(C_BLOCK | C_UNBLOCK); + + if (input_flags & (O_DSYNC | O_SYNC)) + input_flags |= O_RSYNC; + + if (output_flags & O_FULLBLOCK) + { + error (0, 0, "%s: %s", _("invalid output flag"), quote ("fullblock")); + usage (EXIT_FAILURE); + } + + if (skip_B) + input_flags |= O_SKIP_BYTES; + if (input_flags & O_SKIP_BYTES && skip != 0) + { + skip_records = skip / input_blocksize; + skip_bytes = skip % input_blocksize; + } + else if (skip != 0) + skip_records = skip; + + if (count_B) + input_flags |= O_COUNT_BYTES; + if (input_flags & O_COUNT_BYTES && count != INTMAX_MAX) + { + max_records = count / input_blocksize; + max_bytes = count % input_blocksize; + } + else if (count != INTMAX_MAX) + max_records = count; + + if (seek_B) + output_flags |= O_SEEK_BYTES; + if (output_flags & O_SEEK_BYTES && seek != 0) + { + seek_records = seek / output_blocksize; + seek_bytes = seek % output_blocksize; + } + else if (seek != 0) + seek_records = seek; + + /* Warn about partial reads if bs=SIZE is given and iflag=fullblock + is not, and if counting or skipping bytes or using direct I/O. + This helps to avoid confusion with miscounts, and to avoid issues + with direct I/O on GNU/Linux. */ + warn_partial_read = + (! (conversions_mask & C_TWOBUFS) && ! (input_flags & O_FULLBLOCK) + && (skip_records + || (0 < max_records && max_records < INTMAX_MAX) + || (input_flags | output_flags) & O_DIRECT)); + + iread_fnc = ((input_flags & O_FULLBLOCK) + ? iread_fullblock + : iread); + input_flags &= ~O_FULLBLOCK; + + if (multiple_bits_set (conversions_mask & (C_ASCII | C_EBCDIC | C_IBM))) + die (EXIT_FAILURE, 0, _("cannot combine any two of {ascii,ebcdic,ibm}")); + if (multiple_bits_set (conversions_mask & (C_BLOCK | C_UNBLOCK))) + die (EXIT_FAILURE, 0, _("cannot combine block and unblock")); + if (multiple_bits_set (conversions_mask & (C_LCASE | C_UCASE))) + die (EXIT_FAILURE, 0, _("cannot combine lcase and ucase")); + if (multiple_bits_set (conversions_mask & (C_EXCL | C_NOCREAT))) + die (EXIT_FAILURE, 0, _("cannot combine excl and nocreat")); + if (multiple_bits_set (input_flags & (O_DIRECT | O_NOCACHE)) + || multiple_bits_set (output_flags & (O_DIRECT | O_NOCACHE))) + die (EXIT_FAILURE, 0, _("cannot combine direct and nocache")); + + if (input_flags & O_NOCACHE) + { + i_nocache = true; + i_nocache_eof = (max_records == 0 && max_bytes == 0); + input_flags &= ~O_NOCACHE; + } + if (output_flags & O_NOCACHE) + { + o_nocache = true; + o_nocache_eof = (max_records == 0 && max_bytes == 0); + output_flags &= ~O_NOCACHE; + } +} + +/* Fix up translation table. */ + +static void +apply_translations (void) +{ + int i; + + if (conversions_mask & C_ASCII) + translate_charset (ebcdic_to_ascii); + + if (conversions_mask & C_UCASE) + { + for (i = 0; i < 256; i++) + trans_table[i] = toupper (trans_table[i]); + translation_needed = true; + } + else if (conversions_mask & C_LCASE) + { + for (i = 0; i < 256; i++) + trans_table[i] = tolower (trans_table[i]); + translation_needed = true; + } + + if (conversions_mask & C_EBCDIC) + { + translate_charset (ascii_to_ebcdic); + newline_character = ascii_to_ebcdic['\n']; + space_character = ascii_to_ebcdic[' ']; + } + else if (conversions_mask & C_IBM) + { + translate_charset (ascii_to_ibm); + newline_character = ascii_to_ibm['\n']; + space_character = ascii_to_ibm[' ']; + } +} + +/* Apply the character-set translations specified by the user + to the NREAD bytes in BUF. */ + +static void +translate_buffer (char *buf, idx_t nread) +{ + idx_t i; + char *cp; + for (i = nread, cp = buf; i; i--, cp++) + *cp = trans_table[to_uchar (*cp)]; +} + +/* Swap *NREAD bytes in BUF, which should have room for an extra byte + after the end because the swapping is not in-place. If *SAVED_BYTE + is nonnegative, also swap that initial byte from the previous call. + Save the last byte into into *SAVED_BYTE if needed to make the + resulting *NREAD even, and set *SAVED_BYTE to -1 otherwise. + Return the buffer's adjusted start, either BUF or BUF + 1. */ + +static char * +swab_buffer (char *buf, idx_t *nread, int *saved_byte) +{ + if (*nread == 0) + return buf; + + /* Update *SAVED_BYTE, and set PREV_SAVED to its old value. */ + int prev_saved = *saved_byte; + if ((prev_saved < 0) == (*nread & 1)) + { + unsigned char c = buf[--*nread]; + *saved_byte = c; + } + else + *saved_byte = -1; + + /* Do the byte-swapping by moving every other byte two + positions toward the end, working from the end of the buffer + toward the beginning. This way we move only half the data. */ + for (idx_t i = *nread; 1 < i; i -= 2) + buf[i] = buf[i - 2]; + + if (prev_saved < 0) + return buf + 1; + + buf[1] = prev_saved; + ++*nread; + return buf; +} + +/* Add OFFSET to the input offset, setting the overflow flag if + necessary. */ + +static void +advance_input_offset (intmax_t offset) +{ + if (0 <= input_offset && INT_ADD_WRAPV (input_offset, offset, &input_offset)) + input_offset = -1; +} + +/* Throw away RECORDS blocks of BLOCKSIZE bytes plus BYTES bytes on + file descriptor FDESC, which is open with read permission for FILE. + Store up to BLOCKSIZE bytes of the data at a time in IBUF or OBUF, if + necessary. RECORDS or BYTES must be nonzero. If FDESC is + STDIN_FILENO, advance the input offset. Return the number of + records remaining, i.e., that were not skipped because EOF was + reached. If FDESC is STDOUT_FILENO, on return, BYTES is the + remaining bytes in addition to the remaining records. */ + +static intmax_t +skip (int fdesc, char const *file, intmax_t records, idx_t blocksize, + idx_t *bytes) +{ + /* Try lseek and if an error indicates it was an inappropriate operation -- + or if the file offset is not representable as an off_t -- + fall back on using read. */ + + errno = 0; + off_t offset; + if (! INT_MULTIPLY_WRAPV (records, blocksize, &offset) + && ! INT_ADD_WRAPV (offset, *bytes, &offset) + && 0 <= lseek (fdesc, offset, SEEK_CUR)) + { + if (fdesc == STDIN_FILENO) + { + struct stat st; + if (ifstat (STDIN_FILENO, &st) != 0) + die (EXIT_FAILURE, errno, _("cannot fstat %s"), quoteaf (file)); + if (usable_st_size (&st) && 0 <= input_offset + && st.st_size - input_offset < offset) + { + /* When skipping past EOF, return the number of _full_ blocks + * that are not skipped, and set offset to EOF, so the caller + * can determine the requested skip was not satisfied. */ + records = ( offset - st.st_size ) / blocksize; + offset = st.st_size - input_offset; + } + else + records = 0; + advance_input_offset (offset); + } + else + { + records = 0; + *bytes = 0; + } + return records; + } + else + { + int lseek_errno = errno; + + /* The seek request may have failed above if it was too big + (> device size, > max file size, etc.) + Or it may not have been done at all (> OFF_T_MAX). + Therefore try to seek to the end of the file, + to avoid redundant reading. */ + if (lseek (fdesc, 0, SEEK_END) >= 0) + { + /* File is seekable, and we're at the end of it, and + size <= OFF_T_MAX. So there's no point using read to advance. */ + + if (!lseek_errno) + { + /* The original seek was not attempted as offset > OFF_T_MAX. + We should error for write as can't get to the desired + location, even if OFF_T_MAX < max file size. + For read we're not going to read any data anyway, + so we should error for consistency. + It would be nice to not error for /dev/{zero,null} + for any offset, but that's not a significant issue. */ + lseek_errno = EOVERFLOW; + } + + if (fdesc == STDIN_FILENO) + error (0, lseek_errno, _("%s: cannot skip"), quotef (file)); + else + error (0, lseek_errno, _("%s: cannot seek"), quotef (file)); + /* If the file has a specific size and we've asked + to skip/seek beyond the max allowable, then quit. */ + quit (EXIT_FAILURE); + } + /* else file_size && offset > OFF_T_MAX or file ! seekable */ + + char *buf; + if (fdesc == STDIN_FILENO) + { + alloc_ibuf (); + buf = ibuf; + } + else + { + alloc_obuf (); + buf = obuf; + } + + do + { + ssize_t nread = iread_fnc (fdesc, buf, records ? blocksize : *bytes); + if (nread < 0) + { + if (fdesc == STDIN_FILENO) + { + error (0, errno, _("error reading %s"), quoteaf (file)); + if (conversions_mask & C_NOERROR) + print_stats (); + } + else + error (0, lseek_errno, _("%s: cannot seek"), quotef (file)); + quit (EXIT_FAILURE); + } + else if (nread == 0) + break; + else if (fdesc == STDIN_FILENO) + advance_input_offset (nread); + + if (records != 0) + records--; + else + *bytes = 0; + } + while (records || *bytes); + + return records; + } +} + +/* Advance the input by NBYTES if possible, after a read error. + The input file offset may or may not have advanced after the failed + read; adjust it to point just after the bad record regardless. + Return true if successful, or if the input is already known to not + be seekable. */ + +static bool +advance_input_after_read_error (idx_t nbytes) +{ + if (! input_seekable) + { + if (input_seek_errno == ESPIPE) + return true; + errno = input_seek_errno; + } + else + { + off_t offset; + advance_input_offset (nbytes); + if (input_offset < 0) + { + error (0, 0, _("offset overflow while reading file %s"), + quoteaf (input_file)); + return false; + } + offset = lseek (STDIN_FILENO, 0, SEEK_CUR); + if (0 <= offset) + { + off_t diff; + if (offset == input_offset) + return true; + diff = input_offset - offset; + if (! (0 <= diff && diff <= nbytes) && status_level != STATUS_NONE) + error (0, 0, _("warning: invalid file offset after failed read")); + if (0 <= lseek (STDIN_FILENO, diff, SEEK_CUR)) + return true; + if (errno == 0) + error (0, 0, _("cannot work around kernel bug after all")); + } + } + + error (0, errno, _("%s: cannot seek"), quotef (input_file)); + return false; +} + +/* Copy NREAD bytes of BUF, with no conversions. */ + +static void +copy_simple (char const *buf, idx_t nread) +{ + char const *start = buf; /* First uncopied char in BUF. */ + + do + { + idx_t nfree = MIN (nread, output_blocksize - oc); + + memcpy (obuf + oc, start, nfree); + + nread -= nfree; /* Update the number of bytes left to copy. */ + start += nfree; + oc += nfree; + if (oc >= output_blocksize) + write_output (); + } + while (nread != 0); +} + +/* Copy NREAD bytes of BUF, doing conv=block + (pad newline-terminated records to 'conversion_blocksize', + replacing the newline with trailing spaces). */ + +static void +copy_with_block (char const *buf, idx_t nread) +{ + for (idx_t i = nread; i; i--, buf++) + { + if (*buf == newline_character) + { + if (col < conversion_blocksize) + { + idx_t j; + for (j = col; j < conversion_blocksize; j++) + output_char (space_character); + } + col = 0; + } + else + { + if (col == conversion_blocksize) + r_truncate++; + else if (col < conversion_blocksize) + output_char (*buf); + col++; + } + } +} + +/* Copy NREAD bytes of BUF, doing conv=unblock + (replace trailing spaces in 'conversion_blocksize'-sized records + with a newline). */ + +static void +copy_with_unblock (char const *buf, idx_t nread) +{ + static idx_t pending_spaces = 0; + + for (idx_t i = 0; i < nread; i++) + { + char c = buf[i]; + + if (col++ >= conversion_blocksize) + { + col = pending_spaces = 0; /* Wipe out any pending spaces. */ + i--; /* Push the char back; get it later. */ + output_char (newline_character); + } + else if (c == space_character) + pending_spaces++; + else + { + /* 'c' is the character after a run of spaces that were not + at the end of the conversion buffer. Output them. */ + while (pending_spaces) + { + output_char (space_character); + --pending_spaces; + } + output_char (c); + } + } +} + +/* Set the file descriptor flags for FD that correspond to the nonzero bits + in ADD_FLAGS. The file's name is NAME. */ + +static void +set_fd_flags (int fd, int add_flags, char const *name) +{ + /* Ignore file creation flags that are no-ops on file descriptors. */ + add_flags &= ~ (O_NOCTTY | O_NOFOLLOW); + + if (add_flags) + { + int old_flags = fcntl (fd, F_GETFL); + int new_flags = old_flags | add_flags; + bool ok = true; + if (old_flags < 0) + ok = false; + else if (old_flags != new_flags) + { + if (new_flags & (O_DIRECTORY | O_NOLINKS)) + { + /* NEW_FLAGS contains at least one file creation flag that + requires some checking of the open file descriptor. */ + struct stat st; + if (ifstat (fd, &st) != 0) + ok = false; + else if ((new_flags & O_DIRECTORY) && ! S_ISDIR (st.st_mode)) + { + errno = ENOTDIR; + ok = false; + } + else if ((new_flags & O_NOLINKS) && 1 < st.st_nlink) + { + errno = EMLINK; + ok = false; + } + new_flags &= ~ (O_DIRECTORY | O_NOLINKS); + } + + if (ok && old_flags != new_flags + && fcntl (fd, F_SETFL, new_flags) == -1) + ok = false; + } + + if (!ok) + die (EXIT_FAILURE, errno, _("setting flags for %s"), quoteaf (name)); + } +} + +/* The main loop. */ + +static int +dd_copy (void) +{ + char *bufstart; /* Input buffer. */ + ssize_t nread; /* Bytes read in the current block. */ + + /* If nonzero, then the previously read block was partial and + PARTREAD was its size. */ + idx_t partread = 0; + + int exit_status = EXIT_SUCCESS; + idx_t n_bytes_read; + + if (skip_records != 0 || skip_bytes != 0) + { + intmax_t us_bytes; + bool us_bytes_overflow = + (INT_MULTIPLY_WRAPV (skip_records, input_blocksize, &us_bytes) + || INT_ADD_WRAPV (skip_bytes, us_bytes, &us_bytes)); + off_t input_offset0 = input_offset; + intmax_t us_blocks = skip (STDIN_FILENO, input_file, + skip_records, input_blocksize, &skip_bytes); + + /* POSIX doesn't say what to do when dd detects it has been + asked to skip past EOF, so I assume it's non-fatal. + There are 3 reasons why there might be unskipped blocks/bytes: + 1. file is too small + 2. pipe has not enough data + 3. partial reads */ + if ((us_blocks + || (0 <= input_offset + && (us_bytes_overflow + || us_bytes != input_offset - input_offset0))) + && status_level != STATUS_NONE) + { + error (0, 0, + _("%s: cannot skip to specified offset"), quotef (input_file)); + } + } + + if (seek_records != 0 || seek_bytes != 0) + { + idx_t bytes = seek_bytes; + intmax_t write_records = skip (STDOUT_FILENO, output_file, + seek_records, output_blocksize, &bytes); + + if (write_records != 0 || bytes != 0) + { + memset (obuf, 0, write_records ? output_blocksize : bytes); + + do + { + idx_t size = write_records ? output_blocksize : bytes; + if (iwrite (STDOUT_FILENO, obuf, size) != size) + { + error (0, errno, _("writing to %s"), quoteaf (output_file)); + quit (EXIT_FAILURE); + } + + if (write_records != 0) + write_records--; + else + bytes = 0; + } + while (write_records || bytes); + } + } + + if (max_records == 0 && max_bytes == 0) + return exit_status; + + alloc_ibuf (); + alloc_obuf (); + int saved_byte = -1; + + while (true) + { + if (status_level == STATUS_PROGRESS) + { + xtime_t progress_time = gethrxtime (); + if (next_time <= progress_time) + { + print_xfer_stats (progress_time); + next_time += XTIME_PRECISION; + } + } + + if (r_partial + r_full >= max_records + !!max_bytes) + break; + + /* Zero the buffer before reading, so that if we get a read error, + whatever data we are able to read is followed by zeros. + This minimizes data loss. */ + if ((conversions_mask & C_SYNC) && (conversions_mask & C_NOERROR)) + memset (ibuf, + (conversions_mask & (C_BLOCK | C_UNBLOCK)) ? ' ' : '\0', + input_blocksize); + + if (r_partial + r_full >= max_records) + nread = iread_fnc (STDIN_FILENO, ibuf, max_bytes); + else + nread = iread_fnc (STDIN_FILENO, ibuf, input_blocksize); + + if (nread > 0) + { + advance_input_offset (nread); + if (i_nocache) + invalidate_cache (STDIN_FILENO, nread); + } + else if (nread == 0) + { + i_nocache_eof |= i_nocache; + o_nocache_eof |= o_nocache && ! (conversions_mask & C_NOTRUNC); + break; /* EOF. */ + } + else + { + if (!(conversions_mask & C_NOERROR) || status_level != STATUS_NONE) + error (0, errno, _("error reading %s"), quoteaf (input_file)); + + if (conversions_mask & C_NOERROR) + { + print_stats (); + idx_t bad_portion = input_blocksize - partread; + + /* We already know this data is not cached, + but call this so that correct offsets are maintained. */ + invalidate_cache (STDIN_FILENO, bad_portion); + + /* Seek past the bad block if possible. */ + if (!advance_input_after_read_error (bad_portion)) + { + exit_status = EXIT_FAILURE; + + /* Suppress duplicate diagnostics. */ + input_seekable = false; + input_seek_errno = ESPIPE; + } + if ((conversions_mask & C_SYNC) && !partread) + /* Replace the missing input with null bytes and + proceed normally. */ + nread = 0; + else + continue; + } + else + { + /* Write any partial block. */ + exit_status = EXIT_FAILURE; + break; + } + } + + n_bytes_read = nread; + + if (n_bytes_read < input_blocksize) + { + r_partial++; + partread = n_bytes_read; + if (conversions_mask & C_SYNC) + { + if (!(conversions_mask & C_NOERROR)) + /* If C_NOERROR, we zeroed the block before reading. */ + memset (ibuf + n_bytes_read, + (conversions_mask & (C_BLOCK | C_UNBLOCK)) ? ' ' : '\0', + input_blocksize - n_bytes_read); + n_bytes_read = input_blocksize; + } + } + else + { + r_full++; + partread = 0; + } + + if (ibuf == obuf) /* If not C_TWOBUFS. */ + { + idx_t nwritten = iwrite (STDOUT_FILENO, obuf, n_bytes_read); + w_bytes += nwritten; + if (nwritten != n_bytes_read) + { + error (0, errno, _("error writing %s"), quoteaf (output_file)); + return EXIT_FAILURE; + } + else if (n_bytes_read == input_blocksize) + w_full++; + else + w_partial++; + continue; + } + + /* Do any translations on the whole buffer at once. */ + + if (translation_needed) + translate_buffer (ibuf, n_bytes_read); + + if (conversions_mask & C_SWAB) + bufstart = swab_buffer (ibuf, &n_bytes_read, &saved_byte); + else + bufstart = ibuf; + + if (conversions_mask & C_BLOCK) + copy_with_block (bufstart, n_bytes_read); + else if (conversions_mask & C_UNBLOCK) + copy_with_unblock (bufstart, n_bytes_read); + else + copy_simple (bufstart, n_bytes_read); + } + + /* If we have a char left as a result of conv=swab, output it. */ + if (0 <= saved_byte) + { + char saved_char = saved_byte; + if (conversions_mask & C_BLOCK) + copy_with_block (&saved_char, 1); + else if (conversions_mask & C_UNBLOCK) + copy_with_unblock (&saved_char, 1); + else + output_char (saved_char); + } + + if ((conversions_mask & C_BLOCK) && col > 0) + { + /* If the final input line didn't end with a '\n', pad + the output block to 'conversion_blocksize' chars. */ + for (idx_t i = col; i < conversion_blocksize; i++) + output_char (space_character); + } + + if (col && (conversions_mask & C_UNBLOCK)) + { + /* If there was any output, add a final '\n'. */ + output_char (newline_character); + } + + /* Write out the last block. */ + if (oc != 0) + { + idx_t nwritten = iwrite (STDOUT_FILENO, obuf, oc); + w_bytes += nwritten; + if (nwritten != 0) + w_partial++; + if (nwritten != oc) + { + error (0, errno, _("error writing %s"), quoteaf (output_file)); + return EXIT_FAILURE; + } + } + + /* If the last write was converted to a seek, then for a regular file + or shared memory object, ftruncate to extend the size. */ + if (final_op_was_seek) + { + struct stat stdout_stat; + if (ifstat (STDOUT_FILENO, &stdout_stat) != 0) + { + error (0, errno, _("cannot fstat %s"), quoteaf (output_file)); + return EXIT_FAILURE; + } + if (S_ISREG (stdout_stat.st_mode) || S_TYPEISSHM (&stdout_stat)) + { + off_t output_offset = lseek (STDOUT_FILENO, 0, SEEK_CUR); + if (0 <= output_offset && stdout_stat.st_size < output_offset) + { + if (iftruncate (STDOUT_FILENO, output_offset) != 0) + { + error (0, errno, + _("failed to truncate to %" PRIdMAX " bytes" + " in output file %s"), + (intmax_t) output_offset, quoteaf (output_file)); + return EXIT_FAILURE; + } + } + } + } + + /* fdatasync/fsync can take a long time, so issue a final progress + indication now if progress has been made since the previous indication. */ + if (conversions_mask & (C_FDATASYNC | C_FSYNC) + && status_level == STATUS_PROGRESS + && 0 <= reported_w_bytes && reported_w_bytes < w_bytes) + print_xfer_stats (0); + + return exit_status; +} + +/* Synchronize output according to conversions_mask. + Do this even if w_bytes is zero, as fsync and fdatasync + flush out write requests from other processes too. + Clear bits in conversions_mask so that synchronization is done only once. + Return zero if successful, an exit status otherwise. */ + +static int +synchronize_output (void) +{ + int exit_status = 0; + int mask = conversions_mask; + conversions_mask &= ~ (C_FDATASYNC | C_FSYNC); + + if ((mask & C_FDATASYNC) && ifdatasync (STDOUT_FILENO) != 0) + { + if (errno != ENOSYS && errno != EINVAL) + { + error (0, errno, _("fdatasync failed for %s"), quoteaf (output_file)); + exit_status = EXIT_FAILURE; + } + mask |= C_FSYNC; + } + + if ((mask & C_FSYNC) && ifsync (STDOUT_FILENO) != 0) + { + error (0, errno, _("fsync failed for %s"), quoteaf (output_file)); + return EXIT_FAILURE; + } + + return exit_status; +} + +int +main (int argc, char **argv) +{ + int i; + int exit_status; + off_t offset; + + install_signal_handlers (); + + initialize_main (&argc, &argv); + set_program_name (argv[0]); + setlocale (LC_ALL, ""); + bindtextdomain (PACKAGE, LOCALEDIR); + textdomain (PACKAGE); + + /* Arrange to close stdout if parse_long_options exits. */ + atexit (maybe_close_stdout); + + page_size = getpagesize (); + + parse_gnu_standard_options_only (argc, argv, PROGRAM_NAME, PACKAGE, Version, + true, usage, AUTHORS, (char const *) NULL); + close_stdout_required = false; + + /* Initialize translation table to identity translation. */ + for (i = 0; i < 256; i++) + trans_table[i] = i; + + /* Decode arguments. */ + scanargs (argc, argv); + + apply_translations (); + + if (input_file == NULL) + { + input_file = _("standard input"); + set_fd_flags (STDIN_FILENO, input_flags, input_file); + } + else + { + if (ifd_reopen (STDIN_FILENO, input_file, O_RDONLY | input_flags, 0) < 0) + die (EXIT_FAILURE, errno, _("failed to open %s"), + quoteaf (input_file)); + } + + offset = lseek (STDIN_FILENO, 0, SEEK_CUR); + input_seekable = (0 <= offset); + input_offset = MAX (0, offset); + input_seek_errno = errno; + + if (output_file == NULL) + { + output_file = _("standard output"); + set_fd_flags (STDOUT_FILENO, output_flags, output_file); + } + else + { + mode_t perms = MODE_RW_UGO; + int opts + = (output_flags + | (conversions_mask & C_NOCREAT ? 0 : O_CREAT) + | (conversions_mask & C_EXCL ? O_EXCL : 0) + | (seek_records || (conversions_mask & C_NOTRUNC) ? 0 : O_TRUNC)); + + off_t size; + if ((INT_MULTIPLY_WRAPV (seek_records, output_blocksize, &size) + || INT_ADD_WRAPV (seek_bytes, size, &size)) + && !(conversions_mask & C_NOTRUNC)) + die (EXIT_FAILURE, 0, + _("offset too large: " + "cannot truncate to a length of seek=%"PRIdMAX"" + " (%td-byte) blocks"), + seek_records, output_blocksize); + + /* Open the output file with *read* access only if we might + need to read to satisfy a 'seek=' request. If we can't read + the file, go ahead with write-only access; it might work. */ + if ((! seek_records + || ifd_reopen (STDOUT_FILENO, output_file, O_RDWR | opts, perms) < 0) + && (ifd_reopen (STDOUT_FILENO, output_file, O_WRONLY | opts, perms) + < 0)) + die (EXIT_FAILURE, errno, _("failed to open %s"), + quoteaf (output_file)); + + if (seek_records != 0 && !(conversions_mask & C_NOTRUNC)) + { + if (iftruncate (STDOUT_FILENO, size) != 0) + { + /* Complain only when ftruncate fails on a regular file, a + directory, or a shared memory object, as POSIX 1003.1-2004 + specifies ftruncate's behavior only for these file types. + For example, do not complain when Linux kernel 2.4 ftruncate + fails on /dev/fd0. */ + int ftruncate_errno = errno; + struct stat stdout_stat; + if (ifstat (STDOUT_FILENO, &stdout_stat) != 0) + { + error (0, errno, _("cannot fstat %s"), + quoteaf (output_file)); + exit_status = EXIT_FAILURE; + } + else if (S_ISREG (stdout_stat.st_mode) + || S_ISDIR (stdout_stat.st_mode) + || S_TYPEISSHM (&stdout_stat)) + { + intmax_t isize = size; + error (0, ftruncate_errno, + _("failed to truncate to %"PRIdMAX" bytes" + " in output file %s"), + isize, quoteaf (output_file)); + exit_status = EXIT_FAILURE; + } + } + } + } + + start_time = gethrxtime (); + next_time = start_time + XTIME_PRECISION; + + exit_status = dd_copy (); + + int sync_status = synchronize_output (); + if (sync_status) + exit_status = sync_status; + + if (max_records == 0 && max_bytes == 0) + { + /* Special case to invalidate cache to end of file. */ + if (i_nocache && !invalidate_cache (STDIN_FILENO, 0)) + { + error (0, errno, _("failed to discard cache for: %s"), + quotef (input_file)); + exit_status = EXIT_FAILURE; + } + if (o_nocache && !invalidate_cache (STDOUT_FILENO, 0)) + { + error (0, errno, _("failed to discard cache for: %s"), + quotef (output_file)); + exit_status = EXIT_FAILURE; + } + } + else + { + /* Invalidate any pending region or to EOF if appropriate. */ + if (i_nocache || i_nocache_eof) + invalidate_cache (STDIN_FILENO, 0); + if (o_nocache || o_nocache_eof) + invalidate_cache (STDOUT_FILENO, 0); + } + + finish_up (); + main_exit (exit_status); +} |