mirror of
https://git.savannah.gnu.org/git/coreutils.git
synced 2025-09-10 07:59:52 +02:00
dd: counts ending in "B" now count bytes
This implements my suggestion in Bug#54112. * src/dd.c (usage): Document the change. (parse_integer, scanargs): Implement the change. Omit some now-obsolete checks for invalid flags. * tests/dd/bytes.sh: Test the new behavior, while retaining checks for the now-obsolete usage. * tests/dd/nocache_eof.sh: Avoid now-obsolete usage.
This commit is contained in:
6
NEWS
6
NEWS
@@ -60,6 +60,12 @@ GNU coreutils NEWS -*- outline -*-
|
||||
dd now supports the aliases iseek=N for skip=N, and oseek=N for seek=N,
|
||||
like FreeBSD and other operating systems.
|
||||
|
||||
dd now counts bytes instead of blocks if a block count ends in "B".
|
||||
For example, 'dd count=100KiB' now copies 100 KiB of data, not
|
||||
102,400 blocks of data. The flags count_bytes, skip_bytes and
|
||||
seek_bytes are therefore obsolescent and are no longer documented,
|
||||
though they still work.
|
||||
|
||||
timeout --foreground --kill-after=... will now exit with status 137
|
||||
if the kill signal was sent, which is consistent with the behavior
|
||||
when the --foreground option is not specified. This allows users to
|
||||
|
||||
@@ -9268,9 +9268,9 @@ use @var{bytes} as the fixed record length.
|
||||
@opindex skip
|
||||
@opindex iseek
|
||||
Skip @var{n} @samp{ibs}-byte blocks in the input file before copying.
|
||||
With @samp{iflag=skip_bytes}, interpret @var{n}
|
||||
If @var{n} ends in the letter @samp{B}, interpret @var{n}
|
||||
as a byte count rather than a block count.
|
||||
(The @samp{iseek=} spelling is an extension to POSIX.)
|
||||
(@samp{B} and the @samp{iseek=} spelling are GNU extensions to POSIX.)
|
||||
|
||||
@item seek=@var{n}
|
||||
@itemx oseek=@var{n}
|
||||
@@ -9278,16 +9278,17 @@ as a byte count rather than a block count.
|
||||
@opindex oseek
|
||||
Skip @var{n} @samp{obs}-byte blocks in the output file before
|
||||
truncating or copying.
|
||||
With @samp{oflag=seek_bytes}, interpret @var{n}
|
||||
If @var{n} ends in the letter @samp{B}, interpret @var{n}
|
||||
as a byte count rather than a block count.
|
||||
(The @samp{oseek=} spelling is an extension to POSIX.)
|
||||
(@samp{B} and the @samp{oseek=} spelling are GNU extensions to POSIX.)
|
||||
|
||||
@item count=@var{n}
|
||||
@opindex count
|
||||
Copy @var{n} @samp{ibs}-byte blocks from the input file, instead
|
||||
of everything until the end of the file.
|
||||
With @samp{iflag=count_bytes}, interpret @var{n}
|
||||
as a byte count rather than a block count.
|
||||
If @var{n} ends in the letter @samp{B},
|
||||
interpret @var{n} as a byte count rather than a block count;
|
||||
this is a GNU extension to POSIX.
|
||||
If short reads occur, as could be the case
|
||||
when reading from a pipe for example, @samp{iflag=fullblock}
|
||||
ensures that @samp{count=} counts complete input blocks
|
||||
@@ -9627,27 +9628,6 @@ as they may return short reads. In that case,
|
||||
this flag is needed to ensure that a @samp{count=} argument is
|
||||
interpreted as a block count rather than a count of read operations.
|
||||
|
||||
@item count_bytes
|
||||
@opindex count_bytes
|
||||
Interpret the @samp{count=} operand as a byte count,
|
||||
rather than a block count, which allows specifying
|
||||
a length that is not a multiple of the I/O block size.
|
||||
This flag can be used only with @code{iflag}.
|
||||
|
||||
@item skip_bytes
|
||||
@opindex skip_bytes
|
||||
Interpret the @samp{skip=} or @samp{iseek=} operand as a byte count,
|
||||
rather than a block count, which allows specifying
|
||||
an offset that is not a multiple of the I/O block size.
|
||||
This flag can be used only with @code{iflag}.
|
||||
|
||||
@item seek_bytes
|
||||
@opindex seek_bytes
|
||||
Interpret the @samp{seek=} or @samp{oseek=} operand as a byte count,
|
||||
rather than a block count, which allows specifying
|
||||
an offset that is not a multiple of the I/O block size.
|
||||
This flag can be used only with @code{oflag}.
|
||||
|
||||
@end table
|
||||
|
||||
These flags are all GNU extensions to POSIX.
|
||||
@@ -9680,23 +9660,22 @@ should not be too large---values larger than a few megabytes
|
||||
are generally wasteful or (as in the gigabyte..exabyte case) downright
|
||||
counterproductive or error-inducing.
|
||||
|
||||
To process data that is at an offset or size that is not a
|
||||
multiple of the I/O@ block size, you can use the @samp{skip_bytes},
|
||||
@samp{seek_bytes} and @samp{count_bytes} flags. Alternatively
|
||||
the traditional method of separate @command{dd} invocations can be used.
|
||||
To process data with offset or size that is not a multiple of the I/O
|
||||
block size, you can use a numeric string @var{n} that ends in the
|
||||
letter @samp{B}.
|
||||
For example, the following shell commands copy data
|
||||
in 512 KiB blocks between a flash drive and a tape, but do not save
|
||||
or restore a 1 MiB area at the start of the flash drive:
|
||||
in 1 MiB blocks between a flash drive and a tape, but do not save
|
||||
or restore a 512-byte area at the start of the flash drive:
|
||||
|
||||
@example
|
||||
flash=/dev/sda
|
||||
tape=/dev/st0
|
||||
|
||||
# Copy all but the initial 1 MiB from flash to tape.
|
||||
(dd bs=1M skip=1 count=0 && dd bs=512k) <$flash >$tape
|
||||
# Copy all but the initial 512 bytes from flash to tape.
|
||||
dd if=$flash iseek=512B bs=1MiB of=$tape
|
||||
|
||||
# Copy from tape back to flash, leaving initial 1 MiB alone.
|
||||
(dd bs=1M seek=1 count=0 && dd bs=512k) <$tape >$flash
|
||||
# Copy from tape back to flash, leaving initial 512 bytes alone.
|
||||
dd if=$tape bs=1MiB of=$flash oseek=512B
|
||||
@end example
|
||||
|
||||
@cindex ddrescue
|
||||
|
||||
108
src/dd.c
108
src/dd.c
@@ -575,6 +575,7 @@ 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\
|
||||
@@ -638,15 +639,6 @@ 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);
|
||||
|
||||
{
|
||||
printf (_("\
|
||||
@@ -1419,9 +1411,8 @@ parse_symbols (char const *str, struct symbol_value const *table,
|
||||
|
||||
/* Return the value of STR, interpreted as a non-negative decimal integer,
|
||||
optionally multiplied by various values.
|
||||
If STR does not represent a number in this format,
|
||||
set *INVALID to a nonzero error value and return
|
||||
INTMAX_MAX if it is an overflow, an indeterminate value otherwise. */
|
||||
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)
|
||||
@@ -1430,53 +1421,57 @@ parse_integer (char const *str, strtol_error *invalid)
|
||||
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. */
|
||||
uintmax_t n = 0;
|
||||
int indeterminate = 0;
|
||||
uintmax_t n = indeterminate;
|
||||
char *suffix;
|
||||
strtol_error e = xstrtoumax (str, &suffix, 10, &n, "bcEGkKMPTwYZ0");
|
||||
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 == 'x')
|
||||
&& suffix[-1] != 'B' && *suffix == 'B')
|
||||
{
|
||||
strtol_error invalid2 = LONGINT_OK;
|
||||
intmax_t result = parse_integer (suffix + 1, &invalid2);
|
||||
if ((invalid2 & ~LONGINT_OVERFLOW) != LONGINT_OK)
|
||||
{
|
||||
*invalid = invalid2;
|
||||
return result;
|
||||
suffix++;
|
||||
if (!*suffix)
|
||||
e &= ~LONGINT_INVALID_SUFFIX_CHAR;
|
||||
}
|
||||
|
||||
if (INT_MULTIPLY_WRAPV (n, result, &result))
|
||||
if ((e & ~LONGINT_OVERFLOW) == LONGINT_INVALID_SUFFIX_CHAR
|
||||
&& *suffix == 'x' && ! (suffix[-1] == 'B' && strchr (suffix + 1, 'B')))
|
||||
{
|
||||
*invalid = LONGINT_OVERFLOW;
|
||||
return INTMAX_MAX;
|
||||
uintmax_t o;
|
||||
strtol_error f = xstrtoumax (suffix + 1, &suffix, 10, &o, suffixes);
|
||||
if ((f & ~LONGINT_OVERFLOW) != LONGINT_OK)
|
||||
{
|
||||
e = f;
|
||||
result = indeterminate;
|
||||
}
|
||||
|
||||
if (result == 0)
|
||||
else if (INT_MULTIPLY_WRAPV (n, o, &result)
|
||||
|| (result != 0 && ((e | f) & LONGINT_OVERFLOW)))
|
||||
{
|
||||
if (STRPREFIX (str, "0x"))
|
||||
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 ((e | invalid2) & LONGINT_OVERFLOW)
|
||||
}
|
||||
else if (n <= INTMAX_MAX)
|
||||
result = n;
|
||||
else
|
||||
{
|
||||
*invalid = LONGINT_OVERFLOW;
|
||||
return INTMAX_MAX;
|
||||
e = LONGINT_OVERFLOW;
|
||||
result = INTMAX_MAX;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
if (INTMAX_MAX < n)
|
||||
{
|
||||
*invalid = e | LONGINT_OVERFLOW;
|
||||
return INTMAX_MAX;
|
||||
}
|
||||
|
||||
if (e != LONGINT_OK)
|
||||
*invalid = e;
|
||||
return n;
|
||||
return result;
|
||||
}
|
||||
|
||||
/* OPERAND is of the form "X=...". Return true if X is NAME. */
|
||||
@@ -1495,6 +1490,7 @@ scanargs (int argc, char *const *argv)
|
||||
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++)
|
||||
{
|
||||
@@ -1529,6 +1525,7 @@ scanargs (int argc, char *const *argv)
|
||||
{
|
||||
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;
|
||||
@@ -1565,11 +1562,20 @@ scanargs (int argc, char *const *argv)
|
||||
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"),
|
||||
@@ -1615,20 +1621,8 @@ scanargs (int argc, char *const *argv)
|
||||
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 (skip_B)
|
||||
input_flags |= O_SKIP_BYTES;
|
||||
if (input_flags & O_SKIP_BYTES && skip != 0)
|
||||
{
|
||||
skip_records = skip / input_blocksize;
|
||||
@@ -1637,6 +1631,8 @@ scanargs (int argc, char *const *argv)
|
||||
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;
|
||||
@@ -1645,6 +1641,8 @@ scanargs (int argc, char *const *argv)
|
||||
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;
|
||||
|
||||
@@ -18,39 +18,46 @@
|
||||
. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
|
||||
print_ver_ dd
|
||||
|
||||
# count_bytes
|
||||
echo 0123456789abcdefghijklm > in || framework_failure_
|
||||
dd count=14 conv=swab iflag=count_bytes < in > out 2> /dev/null || fail=1
|
||||
case $(cat out) in
|
||||
|
||||
# count bytes
|
||||
for operands in "count=14B" "count=14 iflag=count_bytes"; do
|
||||
dd $operands conv=swab < in > out 2> /dev/null || fail=1
|
||||
case $(cat out) in
|
||||
1032547698badc) ;;
|
||||
*) fail=1 ;;
|
||||
esac
|
||||
esac
|
||||
done
|
||||
|
||||
# skip_bytes
|
||||
echo 0123456789abcdefghijklm > in || framework_failure_
|
||||
dd skip=10 iflag=skip_bytes < in > out 2> /dev/null || fail=1
|
||||
case $(cat out) in
|
||||
for operands in "iseek=10B" "skip=10 iflag=skip_bytes"; do
|
||||
# skip bytes
|
||||
dd $operands < in > out 2> /dev/null || fail=1
|
||||
case $(cat out) in
|
||||
abcdefghijklm) ;;
|
||||
*) fail=1 ;;
|
||||
esac
|
||||
esac
|
||||
|
||||
# skip records and bytes from pipe
|
||||
echo 0123456789abcdefghijklm |
|
||||
dd skip=10 bs=2 iflag=skip_bytes > out 2> /dev/null || fail=1
|
||||
case $(cat out) in
|
||||
# skip records and bytes from pipe
|
||||
echo 0123456789abcdefghijklm |
|
||||
dd $operands bs=2 > out 2> /dev/null || fail=1
|
||||
case $(cat out) in
|
||||
abcdefghijklm) ;;
|
||||
*) fail=1 ;;
|
||||
esac
|
||||
esac
|
||||
done
|
||||
|
||||
# seek bytes
|
||||
echo abcdefghijklm |
|
||||
dd bs=5 seek=8 oflag=seek_bytes > out 2> /dev/null || fail=1
|
||||
printf '\0\0\0\0\0\0\0\0abcdefghijklm\n' > expected
|
||||
compare expected out || fail=1
|
||||
|
||||
# Just truncation, no I/O
|
||||
dd bs=5 seek=8 oflag=seek_bytes of=out2 count=0 2> /dev/null || fail=1
|
||||
truncate -s8 expected2
|
||||
compare expected2 out2 || fail=1
|
||||
printf '\0\0\0\0\0\0\0\0abcdefghijklm\n' > expected
|
||||
|
||||
for operands in "oseek=8B" "seek=8 oflag=seek_bytes"; do
|
||||
# seek bytes
|
||||
echo abcdefghijklm |
|
||||
dd $operands bs=5 > out 2> /dev/null || fail=1
|
||||
compare expected out || fail=1
|
||||
|
||||
# Just truncation, no I/O
|
||||
dd $operands bs=5 of=out2 count=0 2> /dev/null || fail=1
|
||||
compare expected2 out2 || fail=1
|
||||
done
|
||||
|
||||
Exit $fail
|
||||
|
||||
@@ -78,7 +78,7 @@ advised_to_eof || fail=1
|
||||
# Ensure sub page size offsets are handled.
|
||||
# I.e., only page aligned offsets are sent to fadvise.
|
||||
if ! strace -o dd.strace -e fadvise64,fadvise64_64 dd status=none \
|
||||
if=in.f of=out.f bs=1M oflag=direct seek=512 oflag=seek_bytes; then
|
||||
if=in.f of=out.f bs=1M oflag=direct oseek=512B; then
|
||||
warn_ '512 byte aligned O_DIRECT is not supported on this (file) system'
|
||||
# The current file system may not support O_DIRECT,
|
||||
# or older XFS had a page size alignment requirement
|
||||
|
||||
Reference in New Issue
Block a user