summaryrefslogtreecommitdiffstats
path: root/bin
diff options
context:
space:
mode:
authorthomas <thomas@FreeBSD.org>2014-05-07 19:33:29 +0000
committerthomas <thomas@FreeBSD.org>2014-05-07 19:33:29 +0000
commita29159cd7017aa3c4d98805b5cec49b464eb0b7c (patch)
tree4706b9b8d40564ba7f2cf3a7979e41bc05a4ff61 /bin
parent9ea4556f36ce28f396d3100b29e0586d7eeef1c4 (diff)
downloadFreeBSD-src-a29159cd7017aa3c4d98805b5cec49b464eb0b7c.zip
FreeBSD-src-a29159cd7017aa3c4d98805b5cec49b464eb0b7c.tar.gz
(dd_out): Fix handling of all-zeroes block at end of input with
conv=sparse. This change fixes two separate issues observed when the last output block is all zeroes, and conv=sparse is in use. In this case, care must be taken to roll back the last seek and write the entire last zero block at the original offset where it should have occurred: when the destination file is a block device, it is not possible to roll back by just one character as the write would then not be properly aligned. Furthermore, the buffer used to write this last all-zeroes block needs to be properly zeroed-out. This was not the case previously, resulting in a junk data byte appearing instead of a zero in the output stream. PR: bin/189174 PR: bin/189284 Reviewed by: kib MFC after: 2 weeks
Diffstat (limited to 'bin')
-rw-r--r--bin/dd/dd.c67
1 files changed, 43 insertions, 24 deletions
diff --git a/bin/dd/dd.c b/bin/dd/dd.c
index 8048b6a..6b4b4f0 100644
--- a/bin/dd/dd.c
+++ b/bin/dd/dd.c
@@ -51,6 +51,7 @@ __FBSDID("$FreeBSD$");
#include <sys/disklabel.h>
#include <sys/filio.h>
+#include <assert.h>
#include <ctype.h>
#include <err.h>
#include <errno.h>
@@ -77,6 +78,7 @@ STAT st; /* statistics */
void (*cfunc)(void); /* conversion function */
uintmax_t cpy_cnt; /* # of blocks to copy */
static off_t pending = 0; /* pending seek if sparse */
+static off_t last_sp = 0; /* size of last added sparse block */
u_int ddflags = 0; /* conversion options */
size_t cbsz; /* conversion block size */
uintmax_t files_cnt = 1; /* # of files to copy */
@@ -174,6 +176,8 @@ setup(void)
} else if ((in.db = malloc(MAX(in.dbsz, cbsz) + cbsz)) == NULL ||
(out.db = malloc(out.dbsz + cbsz)) == NULL)
err(1, "output buffer");
+
+ /* dbp is the first free position in each buffer. */
in.dbp = in.db;
out.dbp = out.db;
@@ -436,8 +440,15 @@ dd_out(int force)
* we play games with the buffer size, and it's usually a partial write.
*/
outp = out.db;
+
+ /*
+ * If force, first try to write all pending data, else try to write
+ * just one block. Subsequently always write data one full block at
+ * a time at most.
+ */
for (n = force ? out.dbcnt : out.dbsz;; n = out.dbsz) {
- for (cnt = n;; cnt -= nw) {
+ cnt = n;
+ do {
sparse = 0;
if (ddflags & C_SPARSE) {
sparse = 1; /* Is buffer sparse? */
@@ -449,18 +460,24 @@ dd_out(int force)
}
if (sparse && !force) {
pending += cnt;
+ last_sp = cnt;
nw = cnt;
} else {
if (pending != 0) {
- if (force)
- pending--;
+ /* If forced to write, and we have no
+ * data left, we need to write the last
+ * sparse block explicitly.
+ */
+ if (force && cnt == 0) {
+ pending -= last_sp;
+ assert(outp == out.db);
+ memset(outp, 0, cnt);
+ }
if (lseek(out.fd, pending, SEEK_CUR) ==
-1)
err(2, "%s: seek error creating sparse file",
out.name);
- if (force)
- write(out.fd, outp, 1);
- pending = 0;
+ pending = last_sp = 0;
}
if (cnt)
nw = write(out.fd, outp, cnt);
@@ -475,27 +492,29 @@ dd_out(int force)
err(1, "%s", out.name);
nw = 0;
}
+
outp += nw;
st.bytes += nw;
- if ((size_t)nw == n) {
- if (n != out.dbsz)
- ++st.out_part;
- else
- ++st.out_full;
- break;
- }
- ++st.out_part;
- if ((size_t)nw == cnt)
- break;
- if (out.flags & ISTAPE)
- errx(1, "%s: short write on tape device",
- out.name);
- if (out.flags & ISCHR && !warned) {
- warned = 1;
- warnx("%s: short write on character device",
- out.name);
+
+ if ((size_t)nw == n && n == out.dbsz)
+ ++st.out_full;
+ else
+ ++st.out_part;
+
+ if ((size_t) nw != cnt) {
+ if (out.flags & ISTAPE)
+ errx(1, "%s: short write on tape device",
+ out.name);
+ if (out.flags & ISCHR && !warned) {
+ warned = 1;
+ warnx("%s: short write on character device",
+ out.name);
+ }
}
- }
+
+ cnt -= nw;
+ } while (cnt != 0);
+
if ((out.dbcnt -= n) < out.dbsz)
break;
}
OpenPOWER on IntegriCloud