diff options
Diffstat (limited to 'src/dd.c')
-rw-r--r-- | src/dd.c | 1985 |
1 files changed, 1360 insertions, 625 deletions
@@ -1,10 +1,10 @@ /* dd -- convert a file while copying it. - Copyright (C) 85, 90, 91, 1995-2007 Free Software Foundation, Inc. + Copyright (C) 1985-2016 Free Software Foundation, Inc. - This program is free software; you can redistribute it and/or modify + 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 2, or (at your option) - any later version. + 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 @@ -12,8 +12,7 @@ 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, write to the Free Software Foundation, - Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + along with this program. If not, see <http://www.gnu.org/licenses/>. */ /* Written by Paul Rubin, David MacKenzie, and Stuart Kemp. */ @@ -26,25 +25,27 @@ #include <getopt.h> #include "system.h" +#include "close-stream.h" #include "error.h" #include "fd-reopen.h" #include "gethrxtime.h" -#include "getpagesize.h" #include "human.h" #include "long-options.h" #include "quote.h" +#include "verror.h" #include "xstrtol.h" #include "xtime.h" -static void process_signals (void); - -/* The official name of this program (e.g., no `g' prefix). */ +/* The official name of this program (e.g., no 'g' prefix). */ #define PROGRAM_NAME "dd" -#define AUTHORS "Paul Rubin", "David MacKenzie", "Stuart Kemp" +#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. SA_NODEFER and SA_RESETHAND are XSI extensions. */ + present. */ #ifndef SA_NOCLDSTOP # define SA_NOCLDSTOP 0 # define sigprocmask(How, Set, Oset) /* empty */ @@ -53,9 +54,8 @@ static void process_signals (void); # define siginterrupt(sig, flag) /* empty */ # endif #endif -#ifndef SA_NODEFER -# define SA_NODEFER 0 -#endif + +/* NonStop circa 2011 lacks SA_RESETHAND; see Bug#9076. */ #ifndef SA_RESETHAND # define SA_RESETHAND 0 #endif @@ -64,17 +64,26 @@ static void process_signals (void); # 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 max(a, b) ((a) > (b) ? (a) : (b)) #define output_char(c) \ do \ { \ obuf[oc++] = (c); \ if (oc >= output_blocksize) \ - write_output (); \ + write_output (); \ } \ while (0) @@ -117,18 +126,20 @@ enum C_NOCREAT = 010000, C_EXCL = 020000, C_FDATASYNC = 040000, - C_FSYNC = 0100000 + C_FSYNC = 0100000, + + C_SPARSE = 0200000 }; -/* Status bit masks. */ +/* Status levels. */ enum { - STATUS_NOXFER = 01 + STATUS_NONE = 1, + STATUS_NOXFER = 2, + STATUS_DEFAULT = 3, + STATUS_PROGRESS = 4 }; -/* The name this program was run with. */ -char *program_name; - /* The name of the input file, or NULL for the standard input. */ static char const *input_file = NULL; @@ -147,15 +158,29 @@ static size_t output_blocksize = 0; /* Conversion buffer size, in bytes. 0 prevents conversions. */ static size_t conversion_blocksize = 0; -/* Skip this many records of `input_blocksize' bytes before input. */ +/* Skip this many records of 'input_blocksize' bytes before input. */ static uintmax_t skip_records = 0; -/* Skip this many records of `output_blocksize' bytes before output. */ +/* Skip this many bytes before input in addition of 'skip_records' + records. */ +static size_t skip_bytes = 0; + +/* Skip this many records of 'output_blocksize' bytes before output. */ static uintmax_t seek_records = 0; +/* Skip this many bytes in addition to 'seek_records' records before + output. */ +static uintmax_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 uintmax_t max_records = (uintmax_t) -1; +/* Copy this many bytes in addition to 'max_records' records. */ +static size_t max_bytes = 0; + /* Bit vector of conversions to apply. */ static int conversions_mask = 0; @@ -164,7 +189,7 @@ static int input_flags = 0; static int output_flags = 0; /* Status flags for what is printed to stderr. */ -static int status_flags = 0; +static int status_level = STATUS_DEFAULT; /* If nonzero, filter characters through the translation table. */ static bool translation_needed = false; @@ -187,6 +212,12 @@ static uintmax_t w_bytes = 0; /* 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; @@ -195,11 +226,13 @@ static bool input_seekable; static int input_seek_errno; /* File offset of the input, in bytes, along with a flag recording - whether it overflowed. The offset is valid only if the input is - seekable and if the offset has not overflowed. */ + whether it overflowed. */ static uintmax_t input_offset; static bool input_offset_overflow; +/* True if a partial read should be diagnosed. */ +static bool warn_partial_read; + /* Records truncated by conv=block. */ static uintmax_t r_truncate = 0; @@ -208,13 +241,16 @@ static uintmax_t r_truncate = 0; static char newline_character = '\n'; static char space_character = ' '; +/* Input buffer. */ +static char *ibuf; + /* Output buffer. */ static char *obuf; -/* Current index into `obuf'. */ +/* Current index into 'obuf'. */ static size_t oc = 0; -/* Index into current line, for `conv=block' and `conv=unblock'. */ +/* Index into current line, for 'conv=block' and 'conv=unblock'. */ static size_t col = 0; /* The set of signals that are caught. */ @@ -226,8 +262,14 @@ 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; + +/* Function used for read (to handle iflag=fullblock parameter). */ +static ssize_t (*iread_fnc) (int fd, char *buf, size_t size); + /* A longest symbol in the struct symbol_values tables below. */ -#define LONGEST_SYMBOL "fdatasync" +#define LONGEST_SYMBOL "count_bytes" /* A symbol and the corresponding integer value. */ struct symbol_value @@ -239,13 +281,14 @@ struct symbol_value /* Conversion symbols, for conv="...". */ static struct symbol_value const conversions[] = { - {"ascii", C_ASCII | C_TWOBUFS}, /* EBCDIC to ASCII. */ - {"ebcdic", C_EBCDIC | C_TWOBUFS}, /* ASCII to EBCDIC. */ - {"ibm", C_IBM | C_TWOBUFS}, /* Slightly different ASCII to EBCDIC. */ + {"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. */ @@ -257,52 +300,118 @@ static struct symbol_value const conversions[] = {"", 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}, - {"direct", O_DIRECT}, - {"directory", O_DIRECTORY}, - {"dsync", O_DSYNC}, - {"noatime", O_NOATIME}, - {"noctty", O_NOCTTY}, - {"nofollow", HAVE_WORKING_O_NOFOLLOW ? O_NOFOLLOW : 0}, - {"nolinks", O_NOLINKS}, - {"nonblock", O_NONBLOCK}, - {"sync", O_SYNC}, - {"text", O_TEXT}, + {"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', '\117', '\177', '\173', '\133', '\154', '\120', '\175', + '\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', '\112', '\340', '\132', '\137', '\155', + '\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', '\152', '\320', '\241', '\007', + '\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', @@ -312,10 +421,10 @@ static char const ascii_to_ebcdic[] = '\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', + '\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', '\275', '\276', '\277', + '\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' @@ -368,21 +477,21 @@ static char const ebcdic_to_ascii[] = '\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', '\133', '\056', '\074', '\050', '\053', '\041', + '\247', '\250', '\325', '\056', '\074', '\050', '\053', '\174', '\046', '\251', '\252', '\253', '\254', '\255', '\256', '\257', - '\260', '\261', '\135', '\044', '\052', '\051', '\073', '\136', + '\260', '\261', '\041', '\044', '\052', '\051', '\073', '\176', '\055', '\057', '\262', '\263', '\264', '\265', '\266', '\267', - '\270', '\271', '\174', '\054', '\045', '\137', '\076', '\077', + '\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', '\313', '\314', '\315', '\316', '\317', '\320', - '\321', '\176', '\163', '\164', '\165', '\166', '\167', '\170', - '\171', '\172', '\322', '\323', '\324', '\325', '\326', '\327', + '\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', '\345', '\346', '\347', + '\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', @@ -393,43 +502,87 @@ static char const ebcdic_to_ascii[] = '\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 error() but handle any pending newline. */ + +static void _GL_ATTRIBUTE_FORMAT ((__printf__, 3, 4)) +nl_error (int status, int errnum, const char *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) - fprintf (stderr, _("Try `%s --help' for more information.\n"), - program_name); + emit_try_help (); else { printf (_("\ Usage: %s [OPERAND]...\n\ or: %s OPTION\n\ "), - program_name, program_name); + program_name, program_name); fputs (_("\ Copy a file, converting and formatting according to the operands.\n\ \n\ - bs=BYTES force ibs=BYTES and obs=BYTES\n\ + bs=BYTES read and write up to BYTES bytes at a time\n\ cbs=BYTES convert BYTES bytes at a time\n\ conv=CONVS convert the file as per the comma separated symbol list\n\ - count=BLOCKS copy only BLOCKS input blocks\n\ - ibs=BYTES read BYTES bytes at a time\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\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=BLOCKS skip BLOCKS obs-sized blocks at start of output\n\ - skip=BLOCKS skip BLOCKS ibs-sized blocks at start of input\n\ - status=noxfer suppress transfer statistics\n\ + seek=N skip N obs-sized blocks at start of output\n\ + skip=N skip N ibs-sized blocks at start of input\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\ -BLOCKS and BYTES may be followed by the following multiplicative suffixes:\n\ -xM M, c 1, w 2, b 512, kB 1000, K 1024, MB 1000*1000, M 1024*1024,\n\ -GB 1000*1000*1000, G 1024*1024*1024, and so on for T, P, E, Z, Y.\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\ \n\ Each CONV symbol may be:\n\ \n\ @@ -441,18 +594,17 @@ Each CONV symbol may be:\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\ -"), stdout); - fputs (_("\ - nocreat do not create the output file\n\ - excl fail if the output file already exists\n\ - notrunc do not truncate the output file\n\ ucase change lower case to upper case\n\ + sparse try to seek rather than write the output for NUL input 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\ - sync pad every input block with NULs to ibs-size; when used\n\ - with block or unblock, pad with spaces rather than NULs\n\ fdatasync physically write output file data before finishing\n\ fsync likewise, but also write metadata\n\ "), stdout); @@ -462,56 +614,129 @@ 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); + fputs (_(" direct use direct I/O for data\n"), stdout); if (O_DIRECTORY) - fputs (_(" directory fail unless a directory\n"), stdout); + fputs (_(" directory fail unless a directory\n"), stdout); if (O_DSYNC) - fputs (_(" dsync use synchronized I/O for data\n"), stdout); + fputs (_(" dsync use synchronized I/O for data\n"), stdout); if (O_SYNC) - fputs (_(" sync likewise, but also for metadata\n"), stdout); + 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); + fputs (_(" nonblock use non-blocking I/O\n"), stdout); if (O_NOATIME) - fputs (_(" noatime do not update access time\n"), stdout); + 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); + fputs (_(" noctty do not assign controlling terminal from file\n"), + stdout); if (HAVE_WORKING_O_NOFOLLOW) - fputs (_(" nofollow do not follow symlinks\n"), stdout); + fputs (_(" nofollow do not follow symlinks\n"), stdout); if (O_NOLINKS) - fputs (_(" nolinks fail if multiply-linked\n"), stdout); + fputs (_(" nolinks fail if multiply-linked\n"), stdout); if (O_BINARY) - fputs (_(" binary use binary I/O for data\n"), stdout); + fputs (_(" binary use binary I/O for data\n"), stdout); if (O_TEXT) - fputs (_(" text use text I/O for data\n"), stdout); + fputs (_(" text use text I/O for data\n"), stdout); + if (O_COUNT_BYTES) + fputs (_(" count_bytes treat 'count=N' as a byte count (iflag only)\n\ +"), stdout); + if (O_SKIP_BYTES) + fputs (_(" skip_bytes treat 'skip=N' as a byte count (iflag only)\n\ +"), stdout); + if (O_SEEK_BYTES) + fputs (_(" seek_bytes treat 'seek=N' as a byte count (oflag only)\n\ +"), stdout); { - char const *siginfo_name = (SIGINFO == SIGUSR1 ? "USR1" : "INFO"); - printf (_("\ + printf (_("\ \n\ -Sending a %s signal to a running `dd' process makes it\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\ - $ dd if=/dev/zero of=/dev/null& pid=$!\n\ - $ kill -%s $pid; sleep 1; kill $pid\n\ - 18335302+0 records in\n\ - 18335302+0 records out\n\ - 9387674624 bytes (9.4 GB) copied, 34.6279 seconds, 271 MB/s\n\ -\n\ Options are:\n\ \n\ -"), - siginfo_name, siginfo_name); +"), SIGINFO == SIGUSR1 ? "USR1" : "INFO"); } fputs (HELP_OPTION_DESCRIPTION, stdout); fputs (VERSION_OPTION_DESCRIPTION, stdout); - printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT); + 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; + + char *real_buf = malloc (input_blocksize + INPUT_BLOCK_SLOP); + if (!real_buf) + { + uintmax_t ibs = input_blocksize; + char hbuf[LONGEST_HUMAN_READABLE + 1]; + error (EXIT_FAILURE, 0, + _("memory exhausted by input buffer of size %"PRIuMAX + " bytes (%s)"), + ibs, + human_readable (input_blocksize, hbuf, + human_opts | human_base_1024, 1, 1)); + } + + real_buf += SWAB_ALIGN_OFFSET; /* allow space for swab */ + + ibuf = ptr_align (real_buf, page_size); +} + +/* Ensure output buffer OBUF is allocated/initialized. */ + +static void +alloc_obuf (void) +{ + if (obuf) + return; + + if (conversions_mask & C_TWOBUFS) + { + /* Page-align the output buffer, too. */ + char *real_obuf = malloc (output_blocksize + OUTPUT_BLOCK_SLOP); + if (!real_obuf) + { + uintmax_t obs = output_blocksize; + char hbuf[LONGEST_HUMAN_READABLE + 1]; + error (EXIT_FAILURE, 0, + _("memory exhausted by output buffer of size %"PRIuMAX + " bytes (%s)"), + obs, + human_readable (output_blocksize, hbuf, + human_opts | human_base_1024, 1, 1)); + } + obuf = ptr_align (real_obuf, page_size); + } + else + { + alloc_ibuf (); + obuf = ibuf; + } +} + static void translate_charset (char const *new_trans) { @@ -527,55 +752,38 @@ translate_charset (char const *new_trans) static inline bool multiple_bits_set (int i) { - return (i & (i - 1)) != 0; + return MULTIPLE_BITS_SET (i); +} + +static bool +abbreviation_lacks_prefix (char const *message) +{ + return message[strlen (message) - 2] == ' '; } /* Print transfer statistics. */ static void -print_stats (void) +print_xfer_stats (xtime_t progress_time) { - xtime_t now = gethrxtime (); - char hbuf[LONGEST_HUMAN_READABLE + 1]; - int human_opts = - (human_autoscale | human_round_to_nearest - | human_space_before_unit | human_SI | human_B); + xtime_t now = progress_time ? progress_time : gethrxtime (); + char hbuf[3][LONGEST_HUMAN_READABLE + 1]; double delta_s; char const *bytes_per_second; - - fprintf (stderr, - _("%"PRIuMAX"+%"PRIuMAX" records in\n" - "%"PRIuMAX"+%"PRIuMAX" records out\n"), - r_full, r_partial, w_full, w_partial); - - if (r_truncate != 0) - fprintf (stderr, - ngettext ("%"PRIuMAX" truncated record\n", - "%"PRIuMAX" truncated records\n", - select_plural (r_truncate)), - r_truncate); - - if (status_flags & STATUS_NOXFER) - return; + 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. */ - - fprintf (stderr, - ngettext ("%"PRIuMAX" byte (%s) copied", - "%"PRIuMAX" bytes (%s) copied", - select_plural (w_bytes)), - w_bytes, - human_readable (w_bytes, hbuf, human_opts, 1, 1)); - if (start_time < now) { double XTIME_PRECISIONe0 = XTIME_PRECISION; uintmax_t delta_xtime = now; delta_xtime -= start_time; delta_s = delta_xtime / XTIME_PRECISIONe0; - bytes_per_second = human_readable (w_bytes, hbuf, human_opts, - XTIME_PRECISION, delta_xtime); + bytes_per_second = human_readable (w_bytes, hbuf[2], human_opts, + XTIME_PRECISION, delta_xtime); } else { @@ -583,41 +791,71 @@ print_stats (void) bytes_per_second = _("Infinity B"); } - /* TRANSLATORS: The two instances of "s" in this string are the SI - symbol "s" (meaning second), and should not be translated. - - This format used to be: - - ngettext (", %g second, %s/s\n", ", %g seconds, %s/s\n", delta_s == 1) - - but that was incorrect for languages like Polish. To fix this - bug we now use SI symbols even though they're a bit more - confusing in English. */ - fprintf (stderr, _(", %g s, %s/s\n"), delta_s, bytes_per_second); + if (progress_time) + fputc ('\r', stderr); + + /* TRANSLATORS: The instances of "s" in the following formats are + the SI symbol "s" (meaning second), and should not be translated. + The strings use SI symbols for better internationalization even + though they may be a bit more confusing in English. If one of + these formats A looks shorter on the screen than another format + B, then A's string length should be less than B's, and appending + strlen (B) - strlen (A) spaces to A should make it appear to be + at least as long as B. */ + + int stats_len + = (abbreviation_lacks_prefix (si) + ? fprintf (stderr, + ngettext ("%"PRIuMAX" byte copied, %g s, %s/s", + "%"PRIuMAX" bytes copied, %g s, %s/s", + select_plural (w_bytes)), + w_bytes, delta_s, bytes_per_second) + : abbreviation_lacks_prefix (iec) + ? fprintf (stderr, + _("%"PRIuMAX" bytes (%s) copied, %g s, %s/s"), + w_bytes, si, delta_s, bytes_per_second) + : fprintf (stderr, + _("%"PRIuMAX" bytes (%s, %s) copied, %g s, %s/s"), + w_bytes, si, iec, delta_s, bytes_per_second)); + + if (progress_time) + { + if (0 <= stats_len && stats_len < progress_len) + fprintf (stderr, "%*s", progress_len - stats_len, ""); + progress_len = stats_len; + } + else + fputc ('\n', stderr); } static void -cleanup (void) +print_stats (void) { - if (close (STDIN_FILENO) < 0) - error (EXIT_FAILURE, errno, - _("closing input file %s"), quote (input_file)); + if (status_level == STATUS_NONE) + return; - /* Don't remove this call to close, even though close_stdout - closes standard output. This close is necessary when cleanup - is called as part of a signal handler. */ - if (close (STDOUT_FILENO) < 0) - error (EXIT_FAILURE, errno, - _("closing output file %s"), quote (output_file)); -} + if (0 < progress_len) + { + fputc ('\n', stderr); + progress_len = 0; + } -static inline void ATTRIBUTE_NORETURN -quit (int code) -{ - cleanup (); - print_stats (); - process_signals (); - exit (code); + fprintf (stderr, + _("%"PRIuMAX"+%"PRIuMAX" records in\n" + "%"PRIuMAX"+%"PRIuMAX" records out\n"), + r_full, r_partial, w_full, w_partial); + + if (r_truncate != 0) + fprintf (stderr, + ngettext ("%"PRIuMAX" truncated record\n", + "%"PRIuMAX" 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. */ @@ -652,11 +890,7 @@ install_signal_handlers (void) struct sigaction act; sigemptyset (&caught_signals); if (catch_siginfo) - { - sigaction (SIGINFO, NULL, &act); - if (act.sa_handler != SIG_IGN) - sigaddset (&caught_signals, SIGINFO); - } + sigaddset (&caught_signals, SIGINFO); sigaction (SIGINT, NULL, &act); if (act.sa_handler != SIG_IGN) sigaddset (&caught_signals, SIGINT); @@ -665,15 +899,15 @@ install_signal_handlers (void) 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)) { - /* POSIX 1003.1-2001 says SA_RESETHAND implies SA_NODEFER, - but this is not true on Solaris 8 at least. It doesn't - hurt to use SA_NODEFER here, so leave it in. */ act.sa_handler = interrupt_handler; act.sa_flags = SA_NODEFER | SA_RESETHAND; sigaction (SIGINT, &act, NULL); @@ -681,7 +915,7 @@ install_signal_handlers (void) #else - if (catch_siginfo && signal (SIGINFO, SIG_IGN) != SIG_IGN) + if (catch_siginfo) { signal (SIGINFO, siginfo_handler); siginterrupt (SIGINFO, 1); @@ -694,6 +928,21 @@ install_signal_handlers (void) #endif } +static void +cleanup (void) +{ + if (close (STDIN_FILENO) < 0) + error (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 part of a signal handler. */ + if (close (STDOUT_FILENO) < 0) + error (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. */ @@ -701,7 +950,7 @@ install_signal_handlers (void) static void process_signals (void) { - while (interrupt_signal | info_signal_count) + while (interrupt_signal || info_signal_count) { int interrupt; int infos; @@ -710,23 +959,123 @@ process_signals (void) sigprocmask (SIG_BLOCK, &caught_signals, &oldset); /* Reload interrupt_signal and info_signal_count, in case a new - signal was handled before sigprocmask took effect. */ + signal was handled before sigprocmask took effect. */ interrupt = interrupt_signal; infos = info_signal_count; if (infos) - info_signal_count = infos - 1; + info_signal_count = infos - 1; sigprocmask (SIG_SETMASK, &oldset, NULL); if (interrupt) - cleanup (); + cleanup (); print_stats (); if (interrupt) - raise (interrupt); + raise (interrupt); } } +static void +finish_up (void) +{ + cleanup (); + print_stats (); + process_signals (); +} + +static void ATTRIBUTE_NORETURN +quit (int code) +{ + finish_up (); + exit (code); +} + +/* Return LEN rounded down to a multiple of PAGE_SIZE + 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) + { + uintmax_t c_pending = *pending + len; + *pending = c_pending % page_size; + 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; + + /* Minimize syscalls. */ + off_t clen = cache_round (fd, len); + if (len && !clen) + return true; /* Don't advise this time. */ + if (!len && !clen && max_records) + return true; /* Nothing pending. */ + off_t pending = len ? cache_round (fd, 0) : 0; + + if (fd == STDIN_FILENO) + { + if (input_seekable) + { + /* Note we're being careful here to only invalidate what + we've read, so as not to dump any read ahead cache. */ +#if HAVE_POSIX_FADVISE + adv_ret = posix_fadvise (fd, input_offset - clen - pending, clen, + POSIX_FADV_DONTNEED); +#else + errno = ENOTSUP; +#endif + } + else + errno = ESPIPE; + } + else if (fd == STDOUT_FILENO) + { + static off_t output_offset = -2; + + if (output_offset != -1) + { + if (0 > output_offset) + { + output_offset = lseek (fd, 0, SEEK_CUR); + output_offset -= clen + pending; + } + if (0 <= output_offset) + { +#if HAVE_POSIX_FADVISE + adv_ret = posix_fadvise (fd, output_offset, clen, + POSIX_FADV_DONTNEED); +#else + errno = ENOTSUP; +#endif + output_offset += clen + pending; + } + } + } + + 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. */ @@ -734,14 +1083,61 @@ process_signals (void) static ssize_t iread (int fd, char *buf, size_t size) { - for (;;) + ssize_t nread; + + do { - ssize_t nread; process_signals (); nread = read (fd, buf, size); - if (! (nread < 0 && errno == EINTR)) - return nread; } + 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) + { + static ssize_t prev_nread; + + if (0 < prev_nread && prev_nread < size) + { + uintmax_t prev = prev_nread; + if (status_level != STATUS_NONE) + error (0, 0, ngettext (("warning: partial read (%"PRIuMAX" byte); " + "suggest iflag=fullblock"), + ("warning: partial read (%"PRIuMAX" 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, size_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 @@ -754,32 +1150,74 @@ iwrite (int fd, char const *buf, size_t size) { size_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, + here we try to preserve some of its semantics. First, use + posix_fadvise to tell the system not to pollute the buffer + cache with this data. Don't bother to diagnose lseek or + posix_fadvise failure. */ + invalidate_cache (STDOUT_FILENO, 0); + + /* Attempt to ensure that that final block is committed + to disk as quickly as possible. */ + conversions_mask |= C_FSYNC; + } + while (total_written < size) { - ssize_t nwritten; + ssize_t nwritten = 0; process_signals (); - nwritten = write (fd, buf + total_written, size - total_written); + + /* 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; - } + { + 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 1.2.13 on /dev/fd0.) - Set errno to ENOSPC so they get a sensible diagnostic. */ - errno = ENOSPC; - break; - } + { + /* 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; + total_written += nwritten; } + if (o_nocache && total_written) + invalidate_cache (fd, total_written); + return total_written; } -/* Write, then empty, the output buffer `obuf'. */ +/* Write, then empty, the output buffer 'obuf'. */ static void write_output (void) @@ -788,9 +1226,9 @@ write_output (void) w_bytes += nwritten; if (nwritten != output_blocksize) { - error (0, errno, _("writing to %s"), quote (output_file)); + error (0, errno, _("writing to %s"), quoteaf (output_file)); if (nwritten != 0) - w_partial++; + w_partial++; quit (EXIT_FAILURE); } else @@ -798,165 +1236,234 @@ write_output (void) oc = 0; } +/* 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 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...". */ + +static bool _GL_ATTRIBUTE_PURE +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. - As a by product, this function replaces each `,' in STR with a NUL byte. */ + cannot be parsed, use ERROR_MSGID to generate a diagnostic. */ static int -parse_symbols (char *str, struct symbol_value const *table, - char const *error_msgid) +parse_symbols (char const *str, struct symbol_value const *table, + bool exclusive, char const *error_msgid) { int value = 0; - do + while (true) { + char const *strcomma = strchr (str, ','); struct symbol_value const *entry; - char *new = strchr (str, ','); - if (new != NULL) - *new++ = '\0'; - for (entry = table; ; entry++) - { - if (! entry->symbol[0]) - { - error (0, 0, _(error_msgid), quote (str)); - usage (EXIT_FAILURE); - } - if (STREQ (entry->symbol, str)) - { - if (! entry->value) - error (EXIT_FAILURE, 0, _(error_msgid), quote (str)); - value |= entry->value; - break; - } - } - str = new; - } - while (str); + + for (entry = table; + ! (operand_matches (str, entry->symbol, ',') && entry->value); + entry++) + { + if (! entry->symbol[0]) + { + size_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 if STR does not represent a number in this format. */ + Set *INVALID to a nonzero error value if STR does not represent a + number in this format. */ static uintmax_t -parse_integer (const char *str, bool *invalid) +parse_integer (const char *str, strtol_error *invalid) { uintmax_t n; char *suffix; - enum strtol_error e = xstrtoumax (str, &suffix, 10, &n, "bcEGkKMPTwYZ0"); + strtol_error e = xstrtoumax (str, &suffix, 10, &n, "bcEGkKMPTwYZ0"); if (e == LONGINT_INVALID_SUFFIX_CHAR && *suffix == 'x') { uintmax_t multiplier = parse_integer (suffix + 1, invalid); if (multiplier != 0 && n * multiplier / multiplier != n) - { - *invalid = true; - return 0; - } + { + *invalid = LONGINT_OVERFLOW; + return 0; + } n *= multiplier; } else if (e != LONGINT_OK) { - *invalid = true; + *invalid = e; return 0; } return n; } +/* OPERAND is of the form "X=...". Return true if X is NAME. */ + +static bool _GL_ATTRIBUTE_PURE +operand_is (char const *operand, char const *name) +{ + return operand_matches (operand, name, '='); +} + static void -scanargs (int argc, char **argv) +scanargs (int argc, char *const *argv) { int i; size_t blocksize = 0; + uintmax_t count = (uintmax_t) -1; + uintmax_t skip = 0; + uintmax_t seek = 0; for (i = optind; i < argc; i++) { - char *name, *val; + char const *name = argv[i]; + char const *val = strchr (name, '='); - name = argv[i]; - val = strchr (name, '='); if (val == NULL) - { - error (0, 0, _("unrecognized operand %s"), quote (name)); - usage (EXIT_FAILURE); - } - *val++ = '\0'; - - if (STREQ (name, "if")) - input_file = val; - else if (STREQ (name, "of")) - output_file = val; - else if (STREQ (name, "conv")) - conversions_mask |= parse_symbols (val, conversions, - N_("invalid conversion: %s")); - else if (STREQ (name, "iflag")) - input_flags |= parse_symbols (val, flags, - N_("invalid input flag: %s")); - else if (STREQ (name, "oflag")) - output_flags |= parse_symbols (val, flags, - N_("invalid output flag: %s")); - else if (STREQ (name, "status")) - status_flags |= parse_symbols (val, statuses, - N_("invalid status flag: %s")); + { + 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 - { - bool invalid = false; - uintmax_t n = parse_integer (val, &invalid); - - if (STREQ (name, "ibs")) - { - invalid |= ! (0 < n && n <= MAX_BLOCKSIZE (INPUT_BLOCK_SLOP)); - input_blocksize = n; - conversions_mask |= C_TWOBUFS; - } - else if (STREQ (name, "obs")) - { - invalid |= ! (0 < n && n <= MAX_BLOCKSIZE (OUTPUT_BLOCK_SLOP)); - output_blocksize = n; - conversions_mask |= C_TWOBUFS; - } - else if (STREQ (name, "bs")) - { - invalid |= ! (0 < n && n <= MAX_BLOCKSIZE (INPUT_BLOCK_SLOP)); - blocksize = n; - } - else if (STREQ (name, "cbs")) - { - invalid |= ! (0 < n && n <= SIZE_MAX); - conversion_blocksize = n; - } - else if (STREQ (name, "skip")) - skip_records = n; - else if (STREQ (name, "seek")) - seek_records = n; - else if (STREQ (name, "count")) - max_records = n; - else - { - error (0, 0, _("unrecognized operand %s=%s"), - quote_n (0, name), quote_n (1, val)); - usage (EXIT_FAILURE); - } - - if (invalid) - error (EXIT_FAILURE, 0, _("invalid number %s"), quote (val)); - } + { + strtol_error invalid = LONGINT_OK; + uintmax_t n = parse_integer (val, &invalid); + uintmax_t n_min = 0; + uintmax_t n_max = UINTMAX_MAX; + + if (operand_is (name, "ibs")) + { + n_min = 1; + n_max = MAX_BLOCKSIZE (INPUT_BLOCK_SLOP); + input_blocksize = n; + } + else if (operand_is (name, "obs")) + { + n_min = 1; + n_max = MAX_BLOCKSIZE (OUTPUT_BLOCK_SLOP); + output_blocksize = n; + } + else if (operand_is (name, "bs")) + { + n_min = 1; + n_max = MAX_BLOCKSIZE (INPUT_BLOCK_SLOP); + blocksize = n; + } + else if (operand_is (name, "cbs")) + { + n_min = 1; + n_max = SIZE_MAX; + conversion_blocksize = n; + } + else if (operand_is (name, "skip")) + skip = n; + else if (operand_is (name, "seek")) + seek = n; + else if (operand_is (name, "count")) + count = n; + 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) + error (EXIT_FAILURE, invalid == LONGINT_OVERFLOW ? EOVERFLOW : 0, + "%s: %s", _("invalid number"), quote (val)); + } } 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 bs= was given, both `input_blocksize' and `output_blocksize' will - have been set to positive values. If either has not been set, - bs= was not given, so make sure two buffers are used. */ - if (input_blocksize == 0 || output_blocksize == 0) - conversions_mask |= C_TWOBUFS; if (input_blocksize == 0) input_blocksize = DEFAULT_BLOCKSIZE; if (output_blocksize == 0) @@ -967,6 +1474,65 @@ scanargs (int argc, char **argv) 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 (input_flags & O_SEEK_BYTES) + { + error (0, 0, "%s: %s", _("invalid input flag"), quote ("seek_bytes")); + usage (EXIT_FAILURE); + } + + if (output_flags & (O_COUNT_BYTES | O_SKIP_BYTES)) + { + error (0, 0, "%s: %s", _("invalid output flag"), + quote (output_flags & O_COUNT_BYTES + ? "count_bytes" : "skip_bytes")); + usage (EXIT_FAILURE); + } + + 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 (input_flags & O_COUNT_BYTES && count != (uintmax_t) -1) + { + max_records = count / input_blocksize; + max_bytes = count % input_blocksize; + } + else if (count != (uintmax_t) -1) + max_records = count; + + 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 < (uintmax_t) -1) + || (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))) error (EXIT_FAILURE, 0, _("cannot combine any two of {ascii,ebcdic,ibm}")); if (multiple_bits_set (conversions_mask & (C_BLOCK | C_UNBLOCK))) @@ -975,6 +1541,20 @@ scanargs (int argc, char **argv) error (EXIT_FAILURE, 0, _("cannot combine lcase and ucase")); if (multiple_bits_set (conversions_mask & (C_EXCL | C_NOCREAT))) error (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))) + error (EXIT_FAILURE, 0, _("cannot combine direct and nocache")); + + if (input_flags & O_NOCACHE) + { + i_nocache = true; + input_flags &= ~O_NOCACHE; + } + if (output_flags & O_NOCACHE) + { + o_nocache = true; + output_flags &= ~O_NOCACHE; + } } /* Fix up translation table. */ @@ -990,13 +1570,13 @@ apply_translations (void) if (conversions_mask & C_UCASE) { for (i = 0; i < 256; i++) - trans_table[i] = toupper (trans_table[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]); + trans_table[i] = tolower (trans_table[i]); translation_needed = true; } @@ -1027,8 +1607,8 @@ translate_buffer (char *buf, size_t nread) *cp = trans_table[to_uchar (*cp)]; } -/* If true, the last char from the previous call to `swab_buffer' - is saved in `saved_char'. */ +/* If true, the last char from the previous call to 'swab_buffer' + is saved in 'saved_char'. */ static bool char_is_saved = false; /* Odd char from previous call. */ @@ -1094,7 +1674,7 @@ advance_input_offset (uintmax_t offset) to indicate that lseek failed. The offending behavior has been confirmed with an Exabyte SCSI tape - drive accessed via /dev/nst0 on both Linux-2.2.17 and Linux-2.4.16. */ + drive accessed via /dev/nst0 on both Linux 2.2.17 and 2.4.16 kernels. */ #ifdef __linux__ @@ -1120,9 +1700,11 @@ skip_via_lseek (char const *filename, int fdesc, off_t offset, int whence) && ioctl (fdesc, MTIOCGET, &s2) == 0 && MT_SAME_POSITION (s1, s2)) { - error (0, 0, _("warning: working around lseek kernel bug for file (%s)\n\ - of mt_type=0x%0lx -- see <sys/mtio.h> for the list of types"), - filename, s2.mt_type); + if (status_level != STATUS_NONE) + error (0, 0, _("warning: working around lseek kernel bug for file " + "(%s)\n of mt_type=0x%0lx -- " + "see <sys/mtio.h> for the list of types"), + filename, s2.mt_type + 0Lu); errno = 0; new_position = -1; } @@ -1133,18 +1715,20 @@ skip_via_lseek (char const *filename, int fdesc, off_t offset, int whence) # define skip_via_lseek(Filename, Fd, Offset, Whence) lseek (Fd, Offset, Whence) #endif -/* Throw away RECORDS blocks of BLOCKSIZE 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 BUF, if necessary. RECORDS 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. */ +/* 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 uintmax_t skip (int fdesc, char const *file, uintmax_t records, size_t blocksize, - char *buf) + size_t *bytes) { - uintmax_t offset = records * blocksize; + uintmax_t offset = records * blocksize + *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 -- @@ -1155,38 +1739,103 @@ skip (int fdesc, char const *file, uintmax_t records, size_t blocksize, && 0 <= skip_via_lseek (file, fdesc, offset, SEEK_CUR)) { if (fdesc == STDIN_FILENO) - advance_input_offset (offset); - return 0; + { + struct stat st; + if (fstat (STDIN_FILENO, &st) != 0) + error (EXIT_FAILURE, errno, _("cannot fstat %s"), quoteaf (file)); + if (usable_st_size (&st) && 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 ((skip_via_lseek (file, 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 (fdesc, buf, blocksize); - if (nread < 0) - { - if (fdesc == STDIN_FILENO) - { - error (0, errno, _("reading %s"), quote (file)); - if (conversions_mask & C_NOERROR) - { - print_stats (); - continue; - } - } - else - error (0, lseek_errno, _("%s: cannot seek"), quote (file)); - quit (EXIT_FAILURE); - } - - if (nread == 0) - break; - if (fdesc == STDIN_FILENO) - advance_input_offset (nread); - } - while (--records != 0); + { + 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; } @@ -1204,7 +1853,7 @@ advance_input_after_read_error (size_t nbytes) if (! input_seekable) { if (input_seek_errno == ESPIPE) - return true; + return true; errno = input_seek_errno; } else @@ -1213,28 +1862,28 @@ advance_input_after_read_error (size_t nbytes) advance_input_offset (nbytes); input_offset_overflow |= (OFF_T_MAX < input_offset); if (input_offset_overflow) - { - error (0, 0, _("offset overflow while reading file %s"), - quote (input_file)); - return false; - } + { + 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)) - error (0, 0, _("warning: invalid file offset after failed read")); - if (0 <= skip_via_lseek (input_file, 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"), quote (input_file)); + { + 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 <= skip_via_lseek (input_file, 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; } @@ -1255,13 +1904,13 @@ copy_simple (char const *buf, size_t nread) start += nfree; oc += nfree; if (oc >= output_blocksize) - write_output (); + write_output (); } while (nread != 0); } /* Copy NREAD bytes of BUF, doing conv=block - (pad newline-terminated records to `conversion_blocksize', + (pad newline-terminated records to 'conversion_blocksize', replacing the newline with trailing spaces). */ static void @@ -1272,28 +1921,28 @@ copy_with_block (char const *buf, size_t nread) for (i = nread; i; i--, buf++) { if (*buf == newline_character) - { - if (col < conversion_blocksize) - { - size_t j; - for (j = col; j < conversion_blocksize; j++) - output_char (space_character); - } - col = 0; - } + { + if (col < conversion_blocksize) + { + size_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++; - } + { + 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 + (replace trailing spaces in 'conversion_blocksize'-sized records with a newline). */ static void @@ -1308,24 +1957,24 @@ copy_with_unblock (char const *buf, size_t nread) 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); - } + { + 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++; + 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); - } + { + /* '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); + } } } @@ -1344,36 +1993,36 @@ set_fd_flags (int fd, int add_flags, char const *name) int new_flags = old_flags | add_flags; bool ok = true; if (old_flags < 0) - ok = false; + 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 (fstat (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 (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 (fstat (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) - error (EXIT_FAILURE, errno, _("setting flags for %s"), quote (name)); + error (EXIT_FAILURE, errno, _("setting flags for %s"), quoteaf (name)); } } @@ -1382,11 +2031,7 @@ set_fd_flags (int fd, int add_flags, char const *name) static int dd_copy (void) { - char *ibuf, *bufstart; /* Input buffer. */ - /* These are declared static so that even though we don't free the - buffers, valgrind will recognize that there is no "real" leak. */ - static char *real_buf; /* real buffer address before alignment */ - static char *real_obuf; + char *bufstart; /* Input buffer. */ ssize_t nread; /* Bytes read in the current block. */ /* If nonzero, then the previously read block was partial and @@ -1396,7 +2041,7 @@ dd_copy (void) int exit_status = EXIT_SUCCESS; size_t n_bytes_read; - /* Leave at least one extra byte at the beginning and end of `ibuf' + /* Leave at least one extra byte at the beginning and end of 'ibuf' for conv=swab, but keep the buffer address even. But some peculiar device drivers work only with word-aligned buffers, so leave an extra two bytes. */ @@ -1408,163 +2053,194 @@ dd_copy (void) the input buffer; thus we allocate 2 pages of slop in the real buffer. 8k above the blocksize shouldn't bother anyone. - The page alignment is necessary on any linux system that supports + The page alignment is necessary on any Linux kernel that supports either the SGI raw I/O patch or Steven Tweedies raw I/O patch. - It is necessary when accessing raw (i.e. character special) disk + It is necessary when accessing raw (i.e., character special) disk devices on Unixware or other SVR4-derived system. */ - real_buf = xmalloc (input_blocksize + INPUT_BLOCK_SLOP); - ibuf = real_buf; - ibuf += SWAB_ALIGN_OFFSET; /* allow space for swab */ - - ibuf = ptr_align (ibuf, page_size); - - if (conversions_mask & C_TWOBUFS) - { - /* Page-align the output buffer, too. */ - real_obuf = xmalloc (output_blocksize + OUTPUT_BLOCK_SLOP); - obuf = ptr_align (real_obuf, page_size); - } - else + if (skip_records != 0 || skip_bytes != 0) { - real_obuf = NULL; - obuf = ibuf; - } + uintmax_t us_bytes = input_offset + (skip_records * input_blocksize) + + skip_bytes; + uintmax_t us_blocks = skip (STDIN_FILENO, input_file, + skip_records, input_blocksize, &skip_bytes); + us_bytes -= input_offset; - if (skip_records != 0) - { - skip (STDIN_FILENO, input_file, skip_records, input_blocksize, ibuf); /* 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 if the - call to 'skip' returns nonzero. FIXME: maybe give a warning. */ + 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 || (!input_offset_overflow && us_bytes)) + && status_level != STATUS_NONE) + { + error (0, 0, + _("%s: cannot skip to specified offset"), quotef (input_file)); + } } - if (seek_records != 0) + if (seek_records != 0 || seek_bytes != 0) { + size_t bytes = seek_bytes; uintmax_t write_records = skip (STDOUT_FILENO, output_file, - seek_records, output_blocksize, obuf); - - if (write_records != 0) - { - memset (obuf, 0, output_blocksize); - - do - if (iwrite (STDOUT_FILENO, obuf, output_blocksize) - != output_blocksize) - { - error (0, errno, _("writing to %s"), quote (output_file)); - quit (EXIT_FAILURE); - } - while (--write_records != 0); - } + seek_records, output_blocksize, &bytes); + + if (write_records != 0 || bytes != 0) + { + memset (obuf, 0, write_records ? output_blocksize : bytes); + + do + { + size_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) + if (max_records == 0 && max_bytes == 0) return exit_status; + alloc_ibuf (); + alloc_obuf (); + while (1) { - if (r_partial + r_full >= max_records) - break; + 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. */ + 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); + memset (ibuf, + (conversions_mask & (C_BLOCK | C_UNBLOCK)) ? ' ' : '\0', + input_blocksize); - nread = iread (STDIN_FILENO, ibuf, 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 && i_nocache) + invalidate_cache (STDIN_FILENO, nread); if (nread == 0) - break; /* EOF. */ + break; /* EOF. */ if (nread < 0) - { - error (0, errno, _("reading %s"), quote (input_file)); - if (conversions_mask & C_NOERROR) - { - print_stats (); - /* Seek past the bad block if possible. */ - if (!advance_input_after_read_error (input_blocksize - partread)) - { - 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; - } - } + { + 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 (); + size_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; advance_input_offset (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; - } - } + { + 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; - } + { + r_full++; + partread = 0; + } if (ibuf == obuf) /* If not C_TWOBUFS. */ - { - size_t nwritten = iwrite (STDOUT_FILENO, obuf, n_bytes_read); - w_bytes += nwritten; - if (nwritten != n_bytes_read) - { - error (0, errno, _("writing %s"), quote (output_file)); - return EXIT_FAILURE; - } - else if (n_bytes_read == input_blocksize) - w_full++; - else - w_partial++; - continue; - } + { + size_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); + translate_buffer (ibuf, n_bytes_read); if (conversions_mask & C_SWAB) - bufstart = swab_buffer (ibuf, &n_bytes_read); + bufstart = swab_buffer (ibuf, &n_bytes_read); else - bufstart = ibuf; + 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); + copy_with_unblock (bufstart, n_bytes_read); else - copy_simple (bufstart, n_bytes_read); + copy_simple (bufstart, n_bytes_read); } /* If we have a char left as a result of conv=swab, output it. */ @@ -1573,24 +2249,25 @@ dd_copy (void) if (conversions_mask & C_BLOCK) copy_with_block (&saved_char, 1); else if (conversions_mask & C_UNBLOCK) - copy_with_unblock (&saved_char, 1); + copy_with_unblock (&saved_char, 1); else - output_char (saved_char); + 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. */ + the output block to 'conversion_blocksize' chars. */ size_t i; for (i = col; i < conversion_blocksize; i++) - output_char (space_character); + output_char (space_character); } - if ((conversions_mask & C_UNBLOCK) && col == conversion_blocksize) - /* Add a final '\n' if there are exactly `conversion_blocksize' - characters in the final record. */ - output_char (newline_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) @@ -1598,31 +2275,58 @@ dd_copy (void) size_t nwritten = iwrite (STDOUT_FILENO, obuf, oc); w_bytes += nwritten; if (nwritten != 0) - w_partial++; + w_partial++; if (nwritten != oc) - { - error (0, errno, _("writing %s"), quote (output_file)); - return EXIT_FAILURE; - } + { + 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 (fstat (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; + } + } + } } if ((conversions_mask & C_FDATASYNC) && fdatasync (STDOUT_FILENO) != 0) { if (errno != ENOSYS && errno != EINVAL) - { - error (0, errno, _("fdatasync failed for %s"), quote (output_file)); - exit_status = EXIT_FAILURE; - } + { + error (0, errno, _("fdatasync failed for %s"), quoteaf (output_file)); + exit_status = EXIT_FAILURE; + } conversions_mask |= C_FSYNC; } if (conversions_mask & C_FSYNC) while (fsync (STDOUT_FILENO) != 0) if (errno != EINTR) - { - error (0, errno, _("fsync failed for %s"), quote (output_file)); - return EXIT_FAILURE; - } + { + error (0, errno, _("fsync failed for %s"), quoteaf (output_file)); + return EXIT_FAILURE; + } return exit_status; } @@ -1634,19 +2338,23 @@ main (int argc, char **argv) int exit_status; off_t offset; + install_signal_handlers (); + initialize_main (&argc, &argv); - program_name = argv[0]; + set_program_name (argv[0]); setlocale (LC_ALL, ""); bindtextdomain (PACKAGE, LOCALEDIR); textdomain (PACKAGE); /* Arrange to close stdout if parse_long_options exits. */ - atexit (close_stdout); + atexit (maybe_close_stdout); page_size = getpagesize (); - parse_long_options (argc, argv, PROGRAM_NAME, PACKAGE, VERSION, - usage, AUTHORS, (char const *) NULL); + parse_long_options (argc, argv, PROGRAM_NAME, PACKAGE, Version, + usage, AUTHORS, (char const *) NULL); + close_stdout_required = false; + if (getopt_long (argc, argv, "", NULL, NULL) != -1) usage (EXIT_FAILURE); @@ -1666,13 +2374,14 @@ main (int argc, char **argv) } else { - if (fd_reopen (STDIN_FILENO, input_file, O_RDONLY | input_flags, 0) < 0) - error (EXIT_FAILURE, errno, _("opening %s"), quote (input_file)); + if (ifd_reopen (STDIN_FILENO, input_file, O_RDONLY | input_flags, 0) < 0) + error (EXIT_FAILURE, errno, _("failed to open %s"), + quoteaf (input_file)); } offset = lseek (STDIN_FILENO, 0, SEEK_CUR); input_seekable = (0 <= offset); - input_offset = offset; + input_offset = MAX (0, offset); input_seek_errno = errno; if (output_file == NULL) @@ -1682,63 +2391,89 @@ main (int argc, char **argv) } else { - mode_t perms = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; + 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)); + = (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)); /* 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. */ + 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 - || fd_reopen (STDOUT_FILENO, output_file, O_RDWR | opts, perms) < 0) - && (fd_reopen (STDOUT_FILENO, output_file, O_WRONLY | opts, perms) - < 0)) - error (EXIT_FAILURE, errno, _("opening %s"), quote (output_file)); + || ifd_reopen (STDOUT_FILENO, output_file, O_RDWR | opts, perms) < 0) + && (ifd_reopen (STDOUT_FILENO, output_file, O_WRONLY | opts, perms) + < 0)) + error (EXIT_FAILURE, errno, _("failed to open %s"), + quoteaf (output_file)); -#if HAVE_FTRUNCATE if (seek_records != 0 && !(conversions_mask & C_NOTRUNC)) - { - uintmax_t size = seek_records * output_blocksize; - unsigned long int obs = output_blocksize; - - if (OFF_T_MAX / output_blocksize < seek_records) - error (EXIT_FAILURE, 0, - _("offset too large: " - "cannot truncate to a length of seek=%"PRIuMAX"" - " (%lu-byte) blocks"), - seek_records, obs); - - if (ftruncate (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 2.4 ftruncate - fails on /dev/fd0. */ - int ftruncate_errno = errno; - struct stat stdout_stat; - if (fstat (STDOUT_FILENO, &stdout_stat) != 0) - error (EXIT_FAILURE, errno, _("cannot fstat %s"), - quote (output_file)); - if (S_ISREG (stdout_stat.st_mode) - || S_ISDIR (stdout_stat.st_mode) - || S_TYPEISSHM (&stdout_stat)) - error (EXIT_FAILURE, ftruncate_errno, - _("truncating at %"PRIuMAX" bytes in output file %s"), - size, quote (output_file)); - } - } -#endif + { + uintmax_t size = seek_records * output_blocksize + seek_bytes; + unsigned long int obs = output_blocksize; + + if (OFF_T_MAX / output_blocksize < seek_records) + error (EXIT_FAILURE, 0, + _("offset too large: " + "cannot truncate to a length of seek=%"PRIuMAX"" + " (%lu-byte) blocks"), + seek_records, obs); + + 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 (fstat (STDOUT_FILENO, &stdout_stat) != 0) + error (EXIT_FAILURE, errno, _("cannot fstat %s"), + quoteaf (output_file)); + if (S_ISREG (stdout_stat.st_mode) + || S_ISDIR (stdout_stat.st_mode) + || S_TYPEISSHM (&stdout_stat)) + error (EXIT_FAILURE, ftruncate_errno, + _("failed to truncate to %"PRIuMAX" bytes" + " in output file %s"), + size, quoteaf (output_file)); + } + } } - install_signal_handlers (); - start_time = gethrxtime (); + next_time = start_time + XTIME_PRECISION; exit_status = dd_copy (); - quit (exit_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 if (max_records != (uintmax_t) -1) + { + /* Invalidate any pending region less than page size, + in case the kernel might round up. */ + if (i_nocache) + invalidate_cache (STDIN_FILENO, 0); + if (o_nocache) + invalidate_cache (STDOUT_FILENO, 0); + } + + finish_up (); + return exit_status; } |