Although I'll venture you've gotten requests for this sort of modification
before, there previously may not have been a good reason to implement it.
I submit for your consideration a patch (and tests) for 'dd' which will add
two new options which when used permit large bs= values at the same time as
small (less than one block) skip=/seek= values.
The rationale for having the new bskip=/bseek= options where the input skip
and/or output seek is measured in BYTES instead of BLOCKS (ibs or obs,
respectively) is to permit a performance-determined large bs= value to be in
use (128k, for example) but at the same time permit the input bskip= value
to be 4k. Later, when the data transfer is to be accomplished in the reverse
direction, a large bs= value can be used along with a bseek= value of 4k.
The particular application for these options is for backup and restoration
of a large (>8GB) production database where the datafiles are stored on raw
devices (aka raw partitions). The raw devices each have a logical volume
control block (LVCB) of 1k at the beginning (byte offset 0) of the partition,
and the datafiles themselves do not start until byte offset 4k.
The largest possible bs= value (that permits the preservation of the LVCB)
for an un-modified 'dd' is therefore only 4k (bs=4k, skip=1/seek=1).
The data transfer performance using 4k blocks is not good enough for our
purposes (using 512-byte blocks, for instance, the transfer takes more than
36 hours). However, with 128k blocks, the data transfer performance is quite
acceptable (transfer done in generally quite a bit less than 3 hours).
The modified 'dd' has been successfully built and tested on an AIX 4.3 and
on a Redhat 6.1 (kernel 2.2.14-12) systems (gcc-2.95.1 and egcs-2.91.66,
respectively).
- christopher sylvain, systems and database administrator
University of Maryland Medical System
Baltimore, MD 21201
tel. 410-328-1020
*** dd.c.orig Sat Sep 19 13:09:23 1998
--- dd.c Mon Oct 16 13:05:29 2000
***************
*** 93,102 ****
--- 93,108 ----
static uintmax_t skip_records = 0;
/* Skip this many records of `output_blocksize' bytes before output. */
static uintmax_t seek_record = 0;
+ /* Skip this many bytes before input. */
+ static uintmax_t skip_bytes = 0;
+
+ /* Skip this many bytes 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;
/* Bit vector of conversions to apply. */
static int conversions_mask = 0;
***************
*** 298,311 ****
--- 304,322 ----
ibs=BYTES read BYTES bytes at a time\n\
if=FILE read from FILE instead of stdin\n\
obs=BYTES write BYTES bytes at a time\n\
of=FILE write to FILE instead of stdout\n\
seek=BLOCKS skip BLOCKS obs-sized blocks at start of output\n\
+ bseek=BYTES skip BYTES at start of output\n\
skip=BLOCKS skip BLOCKS ibs-sized blocks at start of input\n\
+ bskip=BYTES skip BYTES at start of input\n\
--help display this help and exit\n\
--version output version information and exit\n\
\n\
+ Only one of each of the [b]seek or [b]skip option pairs may be used\n\
+ at any one time.\n\
+ \n\
BYTES may be followed by the following multiplicative suffixes:\n\
xM M, c 1, w 2, b 512, kD 1000, k 1024, MD 1,000,000, M 1,048,576,\n\
GD 1,000,000,000, G 1,073,741,824, and so on for T, P, E, Z, Y.\n\
Each KEYWORD may be:\n\
\n\
***************
*** 319,329 ****
ucase change lower case to upper case\n\
swab swap every pair of input bytes\n\
noerror continue after read errors\n\
sync pad every input block with NULs to ibs-size\n\
"));
! puts (_("\nReport bugs to <[EMAIL PROTECTED]>."));
close_stdout ();
}
exit (status);
}
--- 330,340 ----
ucase change lower case to upper case\n\
swab swap every pair of input bytes\n\
noerror continue after read errors\n\
sync pad every input block with NULs to ibs-size\n\
"));
! puts (_("\nReport bugs to <[EMAIL PROTECTED]>."));
close_stdout ();
}
exit (status);
}
***************
*** 651,663 ****
conversion_blocksize = n;
invalid |= (conversion_blocksize != n
|| conversion_blocksize == 0);
}
else if (STREQ (name, "skip"))
! skip_records = n;
else if (STREQ (name, "seek"))
! seek_record = n;
else if (STREQ (name, "count"))
max_records = n;
else
{
error (0, 0, _("unrecognized option `%s=%s'"), name, val);
--- 662,694 ----
conversion_blocksize = n;
invalid |= (conversion_blocksize != n
|| conversion_blocksize == 0);
}
else if (STREQ (name, "skip"))
! {
! skip_records = n;
! /* may use option only when bskip=BYTES option is not used */
! invalid |= skip_bytes != 0;
! }
else if (STREQ (name, "seek"))
! {
! seek_record = n;
! /* may use option only when bseek=BYTES option is not used */
! invalid |= seek_bytes != 0;
! }
! else if (STREQ (name, "bseek"))
! {
! seek_bytes = n;
! /* may use option only when seek=BLOCKS option is not used */
! invalid |= seek_record != 0;
! }
! else if (STREQ (name, "bskip"))
! {
! skip_bytes = n;
! /* may use option only when skip=BLOCKS option is not used */
! invalid |= skip_records != 0;
! }
else if (STREQ (name, "count"))
max_records = n;
else
{
error (0, 0, _("unrecognized option `%s=%s'"), name, val);
***************
*** 833,842 ****
--- 864,930 ----
break;
}
}
}
+ /* Throw away BYTES on file descriptor FDESC,
+ which is open with read permission for FILE.
+ Store up to BYTES of the data (one BLOCKSIZE at a time) in BUF,
+ if necessary. */
+
+ static void
+ bskip (int fdesc, char *file, uintmax_t bytes, size_t blocksize,
+ unsigned char *buf)
+ {
+ struct stat stats;
+
+ /* Use fstat instead of checking for errno == ESPIPE because
+ lseek doesn't work on some special files but doesn't return an
+ error, either. */
+ /* FIXME: can this really happen? What system? */
+ if (fstat (fdesc, &stats))
+ {
+ error (0, errno, "%s", file);
+ quit (1);
+ }
+
+ /* Try lseek and if an error indicates it was an inappropriate
+ operation, fall back on using read. */
+ if (lseek (fdesc, bytes, SEEK_SET) == -1)
+ {
+ int nread;
+
+ while ((bytes-=blocksize) >= 0)
+ {
+ nread = safe_read (fdesc, buf, blocksize);
+ if (nread < 0)
+ {
+ error (0, errno, "%s", file);
+ quit (1);
+ }
+ /* 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.
+ FIXME: maybe give a warning. */
+ if (nread == 0)
+ break;
+ }
+
+ /* sop up any residue .. */
+ if (bytes < 0)
+ {
+ bytes += blocksize;
+
+ nread = safe_read (fdesc, buf, bytes);
+ if (nread < 0)
+ {
+ error (0, errno, "%s", file);
+ quit (1);
+ }
+ }
+ }
+ }
+
/* Copy NREAD bytes of BUF, with no conversions. */
static void
copy_simple (unsigned char *buf, int nread)
{
***************
*** 952,961 ****
--- 1040,1052 ----
obuf = ibuf;
if (skip_records != 0)
skip (input_fd, input_file, skip_records, input_blocksize, ibuf);
+ if (skip_bytes != 0)
+ bskip (input_fd, input_file, skip_bytes, input_blocksize, ibuf);
+
if (seek_record != 0)
{
/* FIXME: this loses for
% ./dd if=dd seek=1 |:
./dd: a1 standard output: Bad file number
***************
*** 963,972 ****
--- 1054,1066 ----
0+0 records out
*/
skip (output_fd, output_file, seek_record, output_blocksize, obuf);
}
+
+ if (seek_bytes != 0)
+ bskip (output_fd, output_file, seek_bytes, output_blocksize, obuf);
if (max_records == 0)
quit (exit_status);
while (1)
ddtests.tar.gz