summaryrefslogtreecommitdiff
path: root/src/dd.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/dd.c')
-rw-r--r--src/dd.c204
1 files changed, 150 insertions, 54 deletions
diff --git a/src/dd.c b/src/dd.c
index 9d24791aa..e7f40379b 100644
--- a/src/dd.c
+++ b/src/dd.c
@@ -156,12 +156,23 @@ static size_t conversion_blocksize = 0;
/* Skip this many records of 'input_blocksize' bytes before input. */
static uintmax_t skip_records = 0;
+/* 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;
+
/* 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;
@@ -241,7 +252,7 @@ static bool i_nocache, o_nocache;
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
@@ -296,37 +307,55 @@ enum
O_FULLBLOCK = FFS_MASK (v),
v2 = v ^ O_FULLBLOCK,
- O_NOCACHE = FFS_MASK (v2)
+ 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. */
+ {"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}
};
@@ -489,7 +518,7 @@ Copy a file, converting and formatting according to the operands.\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\
+ count=N copy only N input blocks\n\
ibs=BYTES read up to BYTES bytes at a time (default: 512)\n\
"), stdout);
fputs (_("\
@@ -498,8 +527,8 @@ Copy a file, converting and formatting according to the operands.\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\
+ 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=noxfer suppress transfer statistics\n\
"), stdout);
fputs (_("\
@@ -568,6 +597,15 @@ Each FLAG symbol may be:\n\
fputs (_(" binary use binary I/O for data\n"), stdout);
if (O_TEXT)
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");
@@ -1120,6 +1158,9 @@ 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++)
{
@@ -1175,11 +1216,11 @@ scanargs (int argc, char *const *argv)
conversion_blocksize = n;
}
else if (operand_is (name, "skip"))
- skip_records = n;
+ skip = n;
else if (operand_is (name, "seek"))
- seek_records = n;
+ seek = n;
else if (operand_is (name, "count"))
- max_records = n;
+ count = n;
else
{
error (0, 0, _("unrecognized operand %s"), quote (name));
@@ -1216,6 +1257,43 @@ scanargs (int argc, char *const *argv)
usage (EXIT_FAILURE);
}
+ if (input_flags & O_SEEK_BYTES)
+ {
+ error (0, 0, "%s: %s", _("invalid input flag"), "'seek_bytes'");
+ usage (EXIT_FAILURE);
+ }
+
+ if (output_flags & (O_COUNT_BYTES | O_SKIP_BYTES))
+ {
+ error (0, 0, "%s: %s", _("invalid output flag"),
+ 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
@@ -1411,18 +1489,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 BUF, 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, char *buf)
{
- 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 --
@@ -1450,7 +1530,10 @@ skip (int fdesc, char const *file, uintmax_t records, size_t blocksize,
advance_input_offset (offset);
}
else
- records = 0;
+ {
+ records = 0;
+ *bytes = 0;
+ }
return records;
}
else
@@ -1491,29 +1574,30 @@ skip (int fdesc, char const *file, uintmax_t records, size_t blocksize,
do
{
- ssize_t nread = iread_fnc (fdesc, buf, blocksize);
+ ssize_t nread = iread_fnc (fdesc, buf, records ? blocksize : *bytes);
if (nread < 0)
{
if (fdesc == STDIN_FILENO)
{
error (0, errno, _("reading %s"), quote (file));
if (conversions_mask & C_NOERROR)
- {
- print_stats ();
- continue;
- }
+ print_stats ();
}
else
error (0, lseek_errno, _("%s: cannot seek"), quote (file));
quit (EXIT_FAILURE);
}
-
- if (nread == 0)
+ else if (nread == 0)
break;
- if (fdesc == STDIN_FILENO)
+ else if (fdesc == STDIN_FILENO)
advance_input_offset (nread);
+
+ if (records != 0)
+ records--;
+ else
+ *bytes = 0;
}
- while (--records != 0);
+ while (records || *bytes);
return records;
}
@@ -1777,11 +1861,13 @@ dd_copy (void)
obuf = ibuf;
}
- if (skip_records != 0)
+ if (skip_records != 0 || skip_bytes != 0)
{
- uintmax_t us_bytes = input_offset + (skip_records * input_blocksize);
+ 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, ibuf);
+ skip_records, input_blocksize, &skip_bytes,
+ ibuf);
us_bytes -= input_offset;
/* POSIX doesn't say what to do when dd detects it has been
@@ -1797,34 +1883,41 @@ dd_copy (void)
}
}
- 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);
+ seek_records, output_blocksize, &bytes,
+ obuf);
- if (write_records != 0)
+ if (write_records != 0 || bytes != 0)
{
- memset (obuf, 0, output_blocksize);
+ memset (obuf, 0, write_records ? output_blocksize : bytes);
do
{
- if (iwrite (STDOUT_FILENO, obuf, output_blocksize)
- != output_blocksize)
+ size_t size = write_records ? output_blocksize : bytes;
+ if (iwrite (STDOUT_FILENO, obuf, size) != size)
{
error (0, errno, _("writing to %s"), quote (output_file));
quit (EXIT_FAILURE);
}
+
+ if (write_records != 0)
+ write_records--;
+ else
+ bytes = 0;
}
- while (--write_records != 0);
+ while (write_records || bytes);
}
}
- if (max_records == 0)
+ if (max_records == 0 && max_bytes == 0)
return exit_status;
while (1)
{
- if (r_partial + r_full >= max_records)
+ if (r_partial + r_full >= max_records + (max_bytes ? 1 : 0))
break;
/* Zero the buffer before reading, so that if we get a read error,
@@ -1835,7 +1928,10 @@ dd_copy (void)
(conversions_mask & (C_BLOCK | C_UNBLOCK)) ? ' ' : '\0',
input_blocksize);
- nread = iread_fnc (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);