Hi,
The patch is based on this entry
https://bugzilla.redhat.com/show_bug.cgi?id=502026 . It delays
allocation if possible (as suggested in
http://lists.gnu.org/archive/html/bug-coreutils/2009-05/msg00223.html ),
skipping it altogether if dd is not being used for actual copying.
Thanks,
Ondrej
>From f66209859385759510d1a5aa3fe1dadadc913b17 Mon Sep 17 00:00:00 2001
From: Ondrej Oprala <[email protected]>
Date: Tue, 22 Jan 2013 14:21:23 +0100
Subject: [PATCH] dd: Postpone buffer allocations if possible.
* src/dd.c (dd_copy): Move and split the code for buffer
allocations to separate functions and call them conditionally.
(alloc_ibuf): Allocate memory for the input buffer.
(alloc_obuf): Allocate memory for the output buffer.
* tests/dd/no-allocate.sh: New test.
* tests/local.mk: Add the test.
---
src/dd.c | 88 +++++++++++++++++++++++++++++--------------------
tests/dd/no-allocate.sh | 28 ++++++++++++++++
tests/local.mk | 1 +
3 files changed, 82 insertions(+), 35 deletions(-)
create mode 100755 tests/dd/no-allocate.sh
diff --git a/src/dd.c b/src/dd.c
index ef5664b..0a3bede 100644
--- a/src/dd.c
+++ b/src/dd.c
@@ -236,6 +236,9 @@ 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;
@@ -1833,16 +1836,49 @@ human_size (size_t n)
return human_readable (n, hbuf, human_opts, 1, 1);
}
+static void
+alloc_ibuf (void)
+{
+ char *real_buf = malloc (input_blocksize + INPUT_BLOCK_SLOP);
+ if (!real_buf)
+ error (EXIT_FAILURE, 0,
+ _("memory exhausted by input buffer of size %zu bytes (%s)"),
+ input_blocksize, human_size (input_blocksize));
+
+ real_buf += SWAB_ALIGN_OFFSET; /* allow space for swab */
+
+ ibuf = ptr_align (real_buf, page_size);
+}
+
+static void
+alloc_obuf (void)
+{
+ if (conversions_mask & C_TWOBUFS)
+ {
+ /* Page-align the output buffer, too. */
+ char *real_obuf = malloc (output_blocksize + OUTPUT_BLOCK_SLOP);
+ if (!real_obuf)
+ error (EXIT_FAILURE, 0,
+ _("memory exhausted by output buffer of size %zu bytes (%s)"),
+ output_blocksize, human_size (output_blocksize));
+ obuf = ptr_align (real_obuf, page_size);
+ }
+ else
+ obuf = ibuf;
+
+ /* Write a sentinel to the slop after the buffer,
+ to allow efficient checking for NUL blocks. */
+ assert (sizeof (uintptr_t) <= OUTPUT_BLOCK_SLOP);
+ if (obuf)
+ memset (obuf + output_blocksize, 1, sizeof (uintptr_t));
+}
+
/* The main loop. */
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
@@ -1869,37 +1905,14 @@ dd_copy (void)
It is necessary when accessing raw (i.e. character special) disk
devices on Unixware or other SVR4-derived system. */
- real_buf = malloc (input_blocksize + INPUT_BLOCK_SLOP);
- if (!real_buf)
- error (EXIT_FAILURE, 0,
- _("memory exhausted by input buffer of size %zu bytes (%s)"),
- input_blocksize, human_size (input_blocksize));
-
- 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 = malloc (output_blocksize + OUTPUT_BLOCK_SLOP);
- if (!real_obuf)
- error (EXIT_FAILURE, 0,
- _("memory exhausted by output buffer of size %zu bytes (%s)"),
- output_blocksize, human_size (output_blocksize));
- obuf = ptr_align (real_obuf, page_size);
- }
- else
- {
- real_obuf = NULL;
- obuf = ibuf;
- }
+ /* Delay buffer allocation if possible*/
+ if ((skip_records > OFF_T_MAX / input_blocksize)
+ || 0 > skip_via_lseek (input_file, STDIN_FILENO, 0, SEEK_CUR))
+ alloc_ibuf ();
- /* Write a sentinel to the slop after the buffer,
- to allow efficient checking for NUL blocks. */
- assert (sizeof (uintptr_t) <= OUTPUT_BLOCK_SLOP);
- memset (obuf + output_blocksize, 1, sizeof (uintptr_t));
+ if ((seek_records > OFF_T_MAX / output_blocksize)
+ || 0 > skip_via_lseek (output_file, STDOUT_FILENO, 0, SEEK_CUR))
+ alloc_obuf ();
if (skip_records != 0 || skip_bytes != 0)
{
@@ -1955,6 +1968,11 @@ dd_copy (void)
if (max_records == 0 && max_bytes == 0)
return exit_status;
+ if (!ibuf)
+ alloc_ibuf ();
+ if (!obuf)
+ alloc_obuf ();
+
while (1)
{
if (r_partial + r_full >= max_records + !!max_bytes)
diff --git a/tests/dd/no-allocate.sh b/tests/dd/no-allocate.sh
new file mode 100755
index 0000000..e992280
--- /dev/null
+++ b/tests/dd/no-allocate.sh
@@ -0,0 +1,28 @@
+#!/bin/sh
+# make sure that dd doesn't allocate memory unnecessarily
+
+# Copyright (C) 2013 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ dd
+
+#count and skip is zero, we don't need to allocate memory for input block
+(ulimit -v 10000;dd if=/dev/zero of=x bs=10M count=0) || fail=1
+
+#non-skippable input, we need to allocate input block size (and we should fail)
+(ulimit -v 10000; echo "abcde" | dd of=x bs=10M seek=1 skip=1 count=0) &&
fail=1
+
+Exit $fail
diff --git a/tests/local.mk b/tests/local.mk
index 82daee5..e011fdf 100644
--- a/tests/local.mk
+++ b/tests/local.mk
@@ -462,6 +462,7 @@ all_tests = \
tests/df/skip-rootfs.sh \
tests/dd/direct.sh \
tests/dd/misc.sh \
+ tests/dd/no-allocate.sh \
tests/dd/nocache.sh \
tests/dd/not-rewound.sh \
tests/dd/reblock.sh \
--
1.7.11.7