From b52c534bffcc40e5be49f6d012c9d4005f0bbd81 Mon Sep 17 00:00:00 2001 From: Matt Macy Date: Mon, 30 Sep 2019 22:00:48 +0000 Subject: [PATCH] Add iflag=fullblock to dd Normally, count=n means read(2) will be called n times on the input to dd. If the read() returns short, as may happen when reading from a pipe, fewer bytes will be copied from the input. With conv=sync the buffer is padded with zeros to fill the rest of the block. iflag=fullblock causes dd to continue reading until the block is full, so that count=n means n full blocks are copied. This flag is compatible with illumos and GNU dd and is used in the ZFS test suite. Submitted by: Ryan Moeller Reviewed by: manpages, mmacy@ MFC after: 1 week Sponsored by: iXsystems, Inc. Differential Revision: https://reviews.freebsd.org/D21441 --- bin/dd/args.c | 37 ++++++++++++++++++++++++++++++++++++- bin/dd/dd.1 | 16 ++++++++++++++++ bin/dd/dd.c | 51 +++++++++++++++++++++++++++------------------------ bin/dd/dd.h | 1 + 4 files changed, 80 insertions(+), 25 deletions(-) diff --git a/bin/dd/args.c b/bin/dd/args.c index 2624d73094a..3ca49d243ce 100644 --- a/bin/dd/args.c +++ b/bin/dd/args.c @@ -57,6 +57,7 @@ __FBSDID("$FreeBSD$"); static int c_arg(const void *, const void *); static int c_conv(const void *, const void *); +static int c_iflag(const void *, const void *); static int c_oflag(const void *, const void *); static void f_bs(char *); static void f_cbs(char *); @@ -66,6 +67,7 @@ static void f_files(char *); static void f_fillchar(char *); static void f_ibs(char *); static void f_if(char *); +static void f_iflag(char *); static void f_obs(char *); static void f_of(char *); static void f_oflag(char *); @@ -89,6 +91,7 @@ static const struct arg { { "fillchar", f_fillchar, C_FILL, C_FILL }, { "ibs", f_ibs, C_IBS, C_BS|C_IBS }, { "if", f_if, C_IF, C_IF }, + { "iflag", f_iflag, 0, 0 }, { "iseek", f_skip, C_SKIP, C_SKIP }, { "obs", f_obs, C_OBS, C_BS|C_OBS }, { "of", f_of, C_OF, C_OF }, @@ -259,6 +262,38 @@ f_if(char *arg) in.name = arg; } +static const struct iflag { + const char *name; + uint64_t set, noset; +} ilist[] = { + { "fullblock", C_IFULLBLOCK, C_SYNC }, +}; + +static void +f_iflag(char *arg) +{ + struct iflag *ip, tmp; + + while (arg != NULL) { + tmp.name = strsep(&arg, ","); + ip = bsearch(&tmp, ilist, nitems(ilist), sizeof(struct iflag), + c_iflag); + if (ip == NULL) + errx(1, "unknown iflag %s", tmp.name); + if (ddflags & ip->noset) + errx(1, "%s: illegal conversion combination", tmp.name); + ddflags |= ip->set; + } +} + +static int +c_iflag(const void *a, const void *b) +{ + + return (strcmp(((const struct iflag *)a)->name, + ((const struct iflag *)b)->name)); +} + static void f_obs(char *arg) { @@ -339,7 +374,7 @@ static const struct conv { { "parset", C_PARSET, C_PARODD|C_PAREVEN|C_PARNONE, NULL}, { "sparse", C_SPARSE, 0, NULL }, { "swab", C_SWAB, 0, NULL }, - { "sync", C_SYNC, 0, NULL }, + { "sync", C_SYNC, C_IFULLBLOCK, NULL }, { "ucase", C_UCASE, C_LCASE, NULL }, { "unblock", C_UNBLOCK, C_BLOCK, NULL }, }; diff --git a/bin/dd/dd.1 b/bin/dd/dd.1 index e68e0dd8bf0..756a585d3b2 100644 --- a/bin/dd/dd.1 +++ b/bin/dd/dd.1 @@ -102,6 +102,22 @@ bytes instead of the default 512. Read input from .Ar file instead of the standard input. +.It Cm iflag Ns = Ns Ar value Ns Op , Ns Ar value ... +Where +.Cm value +is one of the symbols from the following list. +.Bl -tag -width "fullblock" +.It Cm fullblock +Reading from the input file may not obtain a full block. +When a read returns short, continue reading to fill the block. +Without this flag, +.Cm count +limits the number of times +.Xr read 2 +is called on the input rather than the number of blocks copied in full. +May not be combined with +.Cm conv=sync . +.El .It Cm iseek Ns = Ns Ar n Seek on the input file .Ar n diff --git a/bin/dd/dd.c b/bin/dd/dd.c index 7977d18b4bf..fd228f332b9 100644 --- a/bin/dd/dd.c +++ b/bin/dd/dd.c @@ -408,13 +408,15 @@ dd_in(void) memset(in.dbp, 0, in.dbsz); } - n = read(in.fd, in.dbp, in.dbsz); - if (n == 0) { - in.dbrcnt = 0; - return; - } + in.dbrcnt = 0; +fill: + n = read(in.fd, in.dbp + in.dbrcnt, in.dbsz - in.dbrcnt); - /* Read error. */ + /* EOF */ + if (n == 0 && in.dbrcnt == 0) + return; + + /* Read error */ if (n == -1) { /* * If noerror not specified, die. POSIX requires that @@ -438,26 +440,26 @@ dd_in(void) /* If sync not specified, omit block and continue. */ if (!(ddflags & C_SYNC)) continue; - - /* Read errors count as full blocks. */ - in.dbcnt += in.dbrcnt = in.dbsz; - ++st.in_full; - - /* Handle full input blocks. */ - } else if ((size_t)n == (size_t)in.dbsz) { - in.dbcnt += in.dbrcnt = n; - ++st.in_full; - - /* Handle partial input blocks. */ - } else { - /* If sync, use the entire block. */ - if (ddflags & C_SYNC) - in.dbcnt += in.dbrcnt = in.dbsz; - else - in.dbcnt += in.dbrcnt = n; - ++st.in_part; } + /* If conv=sync, use the entire block. */ + if (ddflags & C_SYNC) + n = in.dbsz; + + /* Count the bytes read for this block. */ + in.dbrcnt += n; + + /* Count the number of full and partial blocks. */ + if (in.dbrcnt == in.dbsz) + ++st.in_full; + else if (ddflags & C_IFULLBLOCK && n != 0) + goto fill; /* these don't count */ + else + ++st.in_part; + + /* Count the total bytes read for this file. */ + in.dbcnt += in.dbrcnt; + /* * POSIX states that if bs is set and no other conversions * than noerror, notrunc or sync are specified, the block @@ -478,6 +480,7 @@ dd_in(void) swapbytes(in.dbp, (size_t)n); } + /* Advance to the next block. */ in.dbp += in.dbrcnt; (*cfunc)(); if (need_summary) diff --git a/bin/dd/dd.h b/bin/dd/dd.h index 893e9965e8d..a39f879830d 100644 --- a/bin/dd/dd.h +++ b/bin/dd/dd.h @@ -104,6 +104,7 @@ typedef struct { #define C_FSYNC 0x0000000080000000ULL #define C_FDATASYNC 0x0000000100000000ULL #define C_OFSYNC 0x0000000200000000ULL +#define C_IFULLBLOCK 0x0000000400000000ULL #define C_PARITY (C_PAREVEN | C_PARODD | C_PARNONE | C_PARSET)