11556Srgrimes/*-
21556Srgrimes * Copyright (c) 1991, 1993, 1994
31556Srgrimes *	The Regents of the University of California.  All rights reserved.
41556Srgrimes *
51556Srgrimes * This code is derived from software contributed to Berkeley by
61556Srgrimes * Keith Muller of the University of California, San Diego and Lance
71556Srgrimes * Visser of Convex Computer Corporation.
81556Srgrimes *
91556Srgrimes * Redistribution and use in source and binary forms, with or without
101556Srgrimes * modification, are permitted provided that the following conditions
111556Srgrimes * are met:
121556Srgrimes * 1. Redistributions of source code must retain the above copyright
131556Srgrimes *    notice, this list of conditions and the following disclaimer.
141556Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
151556Srgrimes *    notice, this list of conditions and the following disclaimer in the
161556Srgrimes *    documentation and/or other materials provided with the distribution.
171556Srgrimes * 4. Neither the name of the University nor the names of its contributors
181556Srgrimes *    may be used to endorse or promote products derived from this software
191556Srgrimes *    without specific prior written permission.
201556Srgrimes *
211556Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
221556Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
231556Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
241556Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
251556Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
261556Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
271556Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
281556Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
291556Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
301556Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
311556Srgrimes * SUCH DAMAGE.
321556Srgrimes */
331556Srgrimes
34114433Sobrien#if 0
351556Srgrimes#ifndef lint
3620420Sstevestatic char const copyright[] =
371556Srgrimes"@(#) Copyright (c) 1991, 1993, 1994\n\
381556Srgrimes	The Regents of the University of California.  All rights reserved.\n";
391556Srgrimes#endif /* not lint */
401556Srgrimes
411556Srgrimes#ifndef lint
4236007Scharnierstatic char sccsid[] = "@(#)dd.c	8.5 (Berkeley) 4/2/94";
43114433Sobrien#endif /* not lint */
4435773Scharnier#endif
4599109Sobrien#include <sys/cdefs.h>
4699109Sobrien__FBSDID("$FreeBSD$");
471556Srgrimes
481556Srgrimes#include <sys/param.h>
491556Srgrimes#include <sys/stat.h>
5050460Sgreen#include <sys/conf.h>
5160859Sobrien#include <sys/disklabel.h>
5250460Sgreen#include <sys/filio.h>
5391807Smarkm#include <sys/time.h>
541556Srgrimes
55266488Sthomas#include <assert.h>
561556Srgrimes#include <ctype.h>
571556Srgrimes#include <err.h>
581556Srgrimes#include <errno.h>
591556Srgrimes#include <fcntl.h>
60111629Smarkm#include <inttypes.h>
6119720Sphk#include <locale.h>
621556Srgrimes#include <stdio.h>
631556Srgrimes#include <stdlib.h>
641556Srgrimes#include <string.h>
651556Srgrimes#include <unistd.h>
661556Srgrimes
671556Srgrimes#include "dd.h"
681556Srgrimes#include "extern.h"
691556Srgrimes
7090108Simpstatic void dd_close(void);
7190108Simpstatic void dd_in(void);
7290108Simpstatic void getfdtype(IO *);
7390108Simpstatic void setup(void);
741556Srgrimes
751556SrgrimesIO	in, out;		/* input/output state */
761556SrgrimesSTAT	st;			/* statistics */
7790108Simpvoid	(*cfunc)(void);		/* conversion function */
78111629Smarkmuintmax_t cpy_cnt;		/* # of blocks to copy */
7991079Smarkmstatic off_t	pending = 0;	/* pending seek if sparse */
80111629Smarkmu_int	ddflags = 0;		/* conversion options */
8151208Sgreensize_t	cbsz;			/* conversion block size */
82111629Smarkmuintmax_t files_cnt = 1;	/* # of files to copy */
8351249Sgreenconst	u_char *ctab;		/* conversion table */
84133762Srwatsonchar	fill_char;		/* Character to fill with if defined */
85250469Seadlervolatile sig_atomic_t need_summary;
861556Srgrimes
871556Srgrimesint
8890108Simpmain(int argc __unused, char *argv[])
891556Srgrimes{
9019720Sphk	(void)setlocale(LC_CTYPE, "");
911556Srgrimes	jcl(argv);
921556Srgrimes	setup();
931556Srgrimes
94250469Seadler	(void)signal(SIGINFO, siginfo_handler);
951556Srgrimes	(void)signal(SIGINT, terminate);
961556Srgrimes
971556Srgrimes	atexit(summary);
981556Srgrimes
991556Srgrimes	while (files_cnt--)
1001556Srgrimes		dd_in();
1011556Srgrimes
1021556Srgrimes	dd_close();
103249063Sbrooks	/*
104249063Sbrooks	 * Some devices such as cfi(4) may perform significant amounts
105249063Sbrooks	 * of work when a write descriptor is closed.  Close the out
106249063Sbrooks	 * descriptor explicitly so that the summary handler (called
107249063Sbrooks	 * from an atexit() hook) includes this work.
108249063Sbrooks	 */
109249063Sbrooks	close(out.fd);
1101556Srgrimes	exit(0);
1111556Srgrimes}
1121556Srgrimes
113126667Sphkstatic int
114126667Sphkparity(u_char c)
115126667Sphk{
116126667Sphk	int i;
117126667Sphk
118126667Sphk	i = c ^ (c >> 1) ^ (c >> 2) ^ (c >> 3) ^
119126667Sphk	    (c >> 4) ^ (c >> 5) ^ (c >> 6) ^ (c >> 7);
120126667Sphk	return (i & 1);
121126667Sphk}
122126667Sphk
1231556Srgrimesstatic void
12490108Simpsetup(void)
1251556Srgrimes{
1261556Srgrimes	u_int cnt;
12719720Sphk	struct timeval tv;
1281556Srgrimes
1291556Srgrimes	if (in.name == NULL) {
1301556Srgrimes		in.name = "stdin";
1311556Srgrimes		in.fd = STDIN_FILENO;
1321556Srgrimes	} else {
13348051Sgreen		in.fd = open(in.name, O_RDONLY, 0);
13448026Sgreen		if (in.fd == -1)
1351556Srgrimes			err(1, "%s", in.name);
1361556Srgrimes	}
1371556Srgrimes
1381556Srgrimes	getfdtype(&in);
1391556Srgrimes
1401556Srgrimes	if (files_cnt > 1 && !(in.flags & ISTAPE))
1411556Srgrimes		errx(1, "files is not supported for non-tape devices");
1421556Srgrimes
1431556Srgrimes	if (out.name == NULL) {
1441556Srgrimes		/* No way to check for read access here. */
1451556Srgrimes		out.fd = STDOUT_FILENO;
1461556Srgrimes		out.name = "stdout";
1471556Srgrimes	} else {
1481556Srgrimes#define	OFLAGS \
1491556Srgrimes    (O_CREAT | (ddflags & (C_SEEK | C_NOTRUNC) ? 0 : O_TRUNC))
1501556Srgrimes		out.fd = open(out.name, O_RDWR | OFLAGS, DEFFILEMODE);
1511556Srgrimes		/*
1521556Srgrimes		 * May not have read access, so try again with write only.
1531556Srgrimes		 * Without read we may have a problem if output also does
1541556Srgrimes		 * not support seeks.
1551556Srgrimes		 */
15648026Sgreen		if (out.fd == -1) {
1571556Srgrimes			out.fd = open(out.name, O_WRONLY | OFLAGS, DEFFILEMODE);
1581556Srgrimes			out.flags |= NOREAD;
1591556Srgrimes		}
16048026Sgreen		if (out.fd == -1)
1611556Srgrimes			err(1, "%s", out.name);
1621556Srgrimes	}
1631556Srgrimes
1641556Srgrimes	getfdtype(&out);
1651556Srgrimes
1661556Srgrimes	/*
1671556Srgrimes	 * Allocate space for the input and output buffers.  If not doing
1681556Srgrimes	 * record oriented I/O, only need a single buffer.
1691556Srgrimes	 */
17051249Sgreen	if (!(ddflags & (C_BLOCK | C_UNBLOCK))) {
1711556Srgrimes		if ((in.db = malloc(out.dbsz + in.dbsz - 1)) == NULL)
17251208Sgreen			err(1, "input buffer");
1731556Srgrimes		out.db = in.db;
17451208Sgreen	} else if ((in.db = malloc(MAX(in.dbsz, cbsz) + cbsz)) == NULL ||
17551208Sgreen	    (out.db = malloc(out.dbsz + cbsz)) == NULL)
17651208Sgreen		err(1, "output buffer");
177266488Sthomas
178266488Sthomas	/* dbp is the first free position in each buffer. */
1791556Srgrimes	in.dbp = in.db;
1801556Srgrimes	out.dbp = out.db;
1811556Srgrimes
1821556Srgrimes	/* Position the input/output streams. */
1831556Srgrimes	if (in.offset)
1841556Srgrimes		pos_in();
1851556Srgrimes	if (out.offset)
1861556Srgrimes		pos_out();
1871556Srgrimes
1881556Srgrimes	/*
18963688Sgreen	 * Truncate the output file.  If it fails on a type of output file
19063688Sgreen	 * that it should _not_ fail on, error out.
1911556Srgrimes	 */
19262311Sgreen	if ((ddflags & (C_OF | C_SEEK | C_NOTRUNC)) == (C_OF | C_SEEK) &&
19362311Sgreen	    out.flags & ISTRUNC)
19462311Sgreen		if (ftruncate(out.fd, out.offset * out.dbsz) == -1)
19562311Sgreen			err(1, "truncating %s", out.name);
1961556Srgrimes
197126667Sphk	if (ddflags & (C_LCASE  | C_UCASE | C_ASCII | C_EBCDIC | C_PARITY)) {
198126667Sphk		if (ctab != NULL) {
199126667Sphk			for (cnt = 0; cnt <= 0377; ++cnt)
200126667Sphk				casetab[cnt] = ctab[cnt];
20146073Simp		} else {
202126667Sphk			for (cnt = 0; cnt <= 0377; ++cnt)
203126667Sphk				casetab[cnt] = cnt;
2045701Sache		}
205126667Sphk		if ((ddflags & C_PARITY) && !(ddflags & C_ASCII)) {
206126667Sphk			/*
207126667Sphk			 * If the input is not EBCDIC, and we do parity
208126667Sphk			 * processing, strip input parity.
209126667Sphk			 */
210126667Sphk			for (cnt = 200; cnt <= 0377; ++cnt)
211126667Sphk				casetab[cnt] = casetab[cnt & 0x7f];
212126667Sphk		}
213126667Sphk		if (ddflags & C_LCASE) {
214126667Sphk			for (cnt = 0; cnt <= 0377; ++cnt)
215126667Sphk				casetab[cnt] = tolower(casetab[cnt]);
216126667Sphk		} else if (ddflags & C_UCASE) {
217126667Sphk			for (cnt = 0; cnt <= 0377; ++cnt)
218126667Sphk				casetab[cnt] = toupper(casetab[cnt]);
219126667Sphk		}
220126667Sphk		if ((ddflags & C_PARITY)) {
221126667Sphk			/*
222126667Sphk			 * This should strictly speaking be a no-op, but I
223126667Sphk			 * wonder what funny LANG settings could get us.
224126667Sphk			 */
225126667Sphk			for (cnt = 0; cnt <= 0377; ++cnt)
226126667Sphk				casetab[cnt] = casetab[cnt] & 0x7f;
227126667Sphk		}
228126667Sphk		if ((ddflags & C_PARSET)) {
229126667Sphk			for (cnt = 0; cnt <= 0377; ++cnt)
230126667Sphk				casetab[cnt] = casetab[cnt] | 0x80;
231126667Sphk		}
232126667Sphk		if ((ddflags & C_PAREVEN)) {
233126667Sphk			for (cnt = 0; cnt <= 0377; ++cnt)
234126667Sphk				if (parity(casetab[cnt]))
235126667Sphk					casetab[cnt] = casetab[cnt] | 0x80;
236126667Sphk		}
237126667Sphk		if ((ddflags & C_PARODD)) {
238126667Sphk			for (cnt = 0; cnt <= 0377; ++cnt)
239126667Sphk				if (!parity(casetab[cnt]))
240126667Sphk					casetab[cnt] = casetab[cnt] | 0x80;
241126667Sphk		}
242126667Sphk
24351208Sgreen		ctab = casetab;
24446073Simp	}
24551208Sgreen
246239991Sed	(void)gettimeofday(&tv, NULL);
247239991Sed	st.start = tv.tv_sec + tv.tv_usec * 1e-6;
2481556Srgrimes}
2491556Srgrimes
2501556Srgrimesstatic void
25190108Simpgetfdtype(IO *io)
2521556Srgrimes{
2531556Srgrimes	struct stat sb;
25450460Sgreen	int type;
2551556Srgrimes
25651208Sgreen	if (fstat(io->fd, &sb) == -1)
2571556Srgrimes		err(1, "%s", io->name);
25862311Sgreen	if (S_ISREG(sb.st_mode))
25962311Sgreen		io->flags |= ISTRUNC;
26050460Sgreen	if (S_ISCHR(sb.st_mode) || S_ISBLK(sb.st_mode)) {
26150487Sgreen		if (ioctl(io->fd, FIODTYPE, &type) == -1) {
26251913Sgreen			err(1, "%s", io->name);
26350487Sgreen		} else {
26450487Sgreen			if (type & D_TAPE)
26550487Sgreen				io->flags |= ISTAPE;
266109873Sphk			else if (type & (D_DISK | D_MEM))
26751212Sgreen				io->flags |= ISSEEK;
26850487Sgreen			if (S_ISCHR(sb.st_mode) && (type & D_TAPE) == 0)
26950487Sgreen				io->flags |= ISCHR;
27050487Sgreen		}
27151335Sgreen		return;
27251335Sgreen	}
27351335Sgreen	errno = 0;
27451335Sgreen	if (lseek(io->fd, (off_t)0, SEEK_CUR) == -1 && errno == ESPIPE)
27551335Sgreen		io->flags |= ISPIPE;
27651335Sgreen	else
27751249Sgreen		io->flags |= ISSEEK;
2781556Srgrimes}
2791556Srgrimes
2801556Srgrimesstatic void
28190108Simpdd_in(void)
2821556Srgrimes{
28348026Sgreen	ssize_t n;
2841556Srgrimes
28528430Sjlemon	for (;;) {
28651335Sgreen		switch (cpy_cnt) {
28751335Sgreen		case -1:			/* count=0 was specified */
2881556Srgrimes			return;
28951335Sgreen		case 0:
29051335Sgreen			break;
29151335Sgreen		default:
292111629Smarkm			if (st.in_full + st.in_part >= (uintmax_t)cpy_cnt)
29351335Sgreen				return;
29451335Sgreen			break;
29551335Sgreen		}
2961556Srgrimes
2971556Srgrimes		/*
29851208Sgreen		 * Zero the buffer first if sync; if doing block operations,
2991556Srgrimes		 * use spaces.
3001556Srgrimes		 */
30148026Sgreen		if (ddflags & C_SYNC) {
302133762Srwatson			if (ddflags & C_FILL)
303133762Srwatson				memset(in.dbp, fill_char, in.dbsz);
304133762Srwatson			else if (ddflags & (C_BLOCK | C_UNBLOCK))
3051556Srgrimes				memset(in.dbp, ' ', in.dbsz);
3061556Srgrimes			else
3071556Srgrimes				memset(in.dbp, 0, in.dbsz);
30848026Sgreen		}
3091556Srgrimes
3101556Srgrimes		n = read(in.fd, in.dbp, in.dbsz);
3111556Srgrimes		if (n == 0) {
3121556Srgrimes			in.dbrcnt = 0;
3131556Srgrimes			return;
3141556Srgrimes		}
3151556Srgrimes
3161556Srgrimes		/* Read error. */
31748026Sgreen		if (n == -1) {
3181556Srgrimes			/*
3191556Srgrimes			 * If noerror not specified, die.  POSIX requires that
3201556Srgrimes			 * the warning message be followed by an I/O display.
3211556Srgrimes			 */
32228430Sjlemon			if (!(ddflags & C_NOERROR))
3231556Srgrimes				err(1, "%s", in.name);
3241556Srgrimes			warn("%s", in.name);
3251556Srgrimes			summary();
3261556Srgrimes
3271556Srgrimes			/*
32851249Sgreen			 * If it's a seekable file descriptor, seek past the
3291556Srgrimes			 * error.  If your OS doesn't do the right thing for
3301556Srgrimes			 * raw disks this section should be modified to re-read
3311556Srgrimes			 * in sector size chunks.
3321556Srgrimes			 */
33351249Sgreen			if (in.flags & ISSEEK &&
3341556Srgrimes			    lseek(in.fd, (off_t)in.dbsz, SEEK_CUR))
3351556Srgrimes				warn("%s", in.name);
3361556Srgrimes
3371556Srgrimes			/* If sync not specified, omit block and continue. */
3381556Srgrimes			if (!(ddflags & C_SYNC))
3391556Srgrimes				continue;
3401556Srgrimes
3411556Srgrimes			/* Read errors count as full blocks. */
3421556Srgrimes			in.dbcnt += in.dbrcnt = in.dbsz;
3431556Srgrimes			++st.in_full;
3441556Srgrimes
3451556Srgrimes		/* Handle full input blocks. */
34662311Sgreen		} else if ((size_t)n == in.dbsz) {
3471556Srgrimes			in.dbcnt += in.dbrcnt = n;
3481556Srgrimes			++st.in_full;
3491556Srgrimes
3501556Srgrimes		/* Handle partial input blocks. */
3511556Srgrimes		} else {
3521556Srgrimes			/* If sync, use the entire block. */
3531556Srgrimes			if (ddflags & C_SYNC)
3541556Srgrimes				in.dbcnt += in.dbrcnt = in.dbsz;
3551556Srgrimes			else
3561556Srgrimes				in.dbcnt += in.dbrcnt = n;
3571556Srgrimes			++st.in_part;
3581556Srgrimes		}
3591556Srgrimes
3601556Srgrimes		/*
3611556Srgrimes		 * POSIX states that if bs is set and no other conversions
3621556Srgrimes		 * than noerror, notrunc or sync are specified, the block
3631556Srgrimes		 * is output without buffering as it is read.
3641556Srgrimes		 */
365249811Skib		if ((ddflags & ~(C_NOERROR | C_NOTRUNC | C_SYNC)) == C_BS) {
3661556Srgrimes			out.dbcnt = in.dbcnt;
3671556Srgrimes			dd_out(1);
3681556Srgrimes			in.dbcnt = 0;
3691556Srgrimes			continue;
3701556Srgrimes		}
3711556Srgrimes
3721556Srgrimes		if (ddflags & C_SWAB) {
37332324Sjoerg			if ((n = in.dbrcnt) & 1) {
3741556Srgrimes				++st.swab;
3751556Srgrimes				--n;
3761556Srgrimes			}
37762311Sgreen			swab(in.dbp, in.dbp, (size_t)n);
3781556Srgrimes		}
3791556Srgrimes
3801556Srgrimes		in.dbp += in.dbrcnt;
3811556Srgrimes		(*cfunc)();
382250469Seadler		if (need_summary) {
383250469Seadler			summary();
384250469Seadler		}
3851556Srgrimes	}
3861556Srgrimes}
3871556Srgrimes
3881556Srgrimes/*
38951249Sgreen * Clean up any remaining I/O and flush output.  If necessary, the output file
3901556Srgrimes * is truncated.
3911556Srgrimes */
3921556Srgrimesstatic void
39390108Simpdd_close(void)
3941556Srgrimes{
3951556Srgrimes	if (cfunc == def)
3961556Srgrimes		def_close();
3971556Srgrimes	else if (cfunc == block)
3981556Srgrimes		block_close();
3991556Srgrimes	else if (cfunc == unblock)
4001556Srgrimes		unblock_close();
40128430Sjlemon	if (ddflags & C_OSYNC && out.dbcnt && out.dbcnt < out.dbsz) {
402133762Srwatson		if (ddflags & C_FILL)
403133762Srwatson			memset(out.dbp, fill_char, out.dbsz - out.dbcnt);
404133762Srwatson		else if (ddflags & (C_BLOCK | C_UNBLOCK))
40528430Sjlemon			memset(out.dbp, ' ', out.dbsz - out.dbcnt);
40628430Sjlemon		else
40728430Sjlemon			memset(out.dbp, 0, out.dbsz - out.dbcnt);
4081556Srgrimes		out.dbcnt = out.dbsz;
4091556Srgrimes	}
41030312Sjoerg	if (out.dbcnt || pending)
4111556Srgrimes		dd_out(1);
412298258Sthomas
413298258Sthomas	/*
414298258Sthomas	 * If the file ends with a hole, ftruncate it to extend its size
415298258Sthomas	 * up to the end of the hole (without having to write any data).
416298258Sthomas	 */
417298258Sthomas	if (out.seek_offset > 0 && (out.flags & ISTRUNC)) {
418298258Sthomas		if (ftruncate(out.fd, out.seek_offset) == -1)
419298258Sthomas			err(1, "truncating %s", out.name);
420298258Sthomas	}
4211556Srgrimes}
4221556Srgrimes
4231556Srgrimesvoid
42490108Simpdd_out(int force)
4251556Srgrimes{
42651208Sgreen	u_char *outp;
42751208Sgreen	size_t cnt, i, n;
42851208Sgreen	ssize_t nw;
4291556Srgrimes	static int warned;
43051208Sgreen	int sparse;
4311556Srgrimes
4321556Srgrimes	/*
4331556Srgrimes	 * Write one or more blocks out.  The common case is writing a full
4341556Srgrimes	 * output block in a single write; increment the full block stats.
4351556Srgrimes	 * Otherwise, we're into partial block writes.  If a partial write,
4361556Srgrimes	 * and it's a character device, just warn.  If a tape device, quit.
4371556Srgrimes	 *
4381556Srgrimes	 * The partial writes represent two cases.  1: Where the input block
4391556Srgrimes	 * was less than expected so the output block was less than expected.
4401556Srgrimes	 * 2: Where the input block was the right size but we were forced to
4411556Srgrimes	 * write the block in multiple chunks.  The original versions of dd(1)
4421556Srgrimes	 * never wrote a block in more than a single write, so the latter case
4431556Srgrimes	 * never happened.
4441556Srgrimes	 *
4451556Srgrimes	 * One special case is if we're forced to do the write -- in that case
4461556Srgrimes	 * we play games with the buffer size, and it's usually a partial write.
4471556Srgrimes	 */
4481556Srgrimes	outp = out.db;
449266488Sthomas
450266488Sthomas	/*
451266488Sthomas	 * If force, first try to write all pending data, else try to write
452266488Sthomas	 * just one block. Subsequently always write data one full block at
453266488Sthomas	 * a time at most.
454266488Sthomas	 */
4551556Srgrimes	for (n = force ? out.dbcnt : out.dbsz;; n = out.dbsz) {
456266488Sthomas		cnt = n;
457266488Sthomas		do {
45830312Sjoerg			sparse = 0;
45930312Sjoerg			if (ddflags & C_SPARSE) {
46030312Sjoerg				sparse = 1;	/* Is buffer sparse? */
46130312Sjoerg				for (i = 0; i < cnt; i++)
46230312Sjoerg					if (outp[i] != 0) {
46330312Sjoerg						sparse = 0;
46430312Sjoerg						break;
46530312Sjoerg					}
46630312Sjoerg			}
46730312Sjoerg			if (sparse && !force) {
46830312Sjoerg				pending += cnt;
46930312Sjoerg				nw = cnt;
47030312Sjoerg			} else {
47130312Sjoerg				if (pending != 0) {
472298258Sthomas					/*
473298258Sthomas					 * Seek past hole.  Note that we need to record the
474298258Sthomas					 * reached offset, because we might have no more data
475298258Sthomas					 * to write, in which case we'll need to call
476298258Sthomas					 * ftruncate to extend the file size.
477266488Sthomas					 */
478298258Sthomas					out.seek_offset = lseek(out.fd, pending, SEEK_CUR);
479298258Sthomas					if (out.seek_offset == -1)
48048051Sgreen						err(2, "%s: seek error creating sparse file",
48148051Sgreen						    out.name);
482298258Sthomas					pending = 0;
48330312Sjoerg				}
484298258Sthomas				if (cnt) {
48530312Sjoerg					nw = write(out.fd, outp, cnt);
486298258Sthomas					out.seek_offset = 0;
487298258Sthomas				} else {
48830312Sjoerg					return;
489298258Sthomas				}
49030312Sjoerg			}
49130312Sjoerg
4921556Srgrimes			if (nw <= 0) {
4931556Srgrimes				if (nw == 0)
4941556Srgrimes					errx(1, "%s: end of device", out.name);
4951556Srgrimes				if (errno != EINTR)
4961556Srgrimes					err(1, "%s", out.name);
4971556Srgrimes				nw = 0;
4981556Srgrimes			}
499266488Sthomas
5001556Srgrimes			outp += nw;
5011556Srgrimes			st.bytes += nw;
502266488Sthomas
503266488Sthomas			if ((size_t)nw == n && n == out.dbsz)
504266488Sthomas				++st.out_full;
505266488Sthomas			else
506266488Sthomas				++st.out_part;
507266488Sthomas
508266488Sthomas			if ((size_t) nw != cnt) {
509266488Sthomas				if (out.flags & ISTAPE)
510266488Sthomas					errx(1, "%s: short write on tape device",
511266488Sthomas				    	out.name);
512266488Sthomas				if (out.flags & ISCHR && !warned) {
513266488Sthomas					warned = 1;
514266488Sthomas					warnx("%s: short write on character device",
515266488Sthomas				    	out.name);
516266488Sthomas				}
5171556Srgrimes			}
518266488Sthomas
519266488Sthomas			cnt -= nw;
520266488Sthomas		} while (cnt != 0);
521266488Sthomas
5221556Srgrimes		if ((out.dbcnt -= n) < out.dbsz)
5231556Srgrimes			break;
5241556Srgrimes	}
5251556Srgrimes
5261556Srgrimes	/* Reassemble the output block. */
5271556Srgrimes	if (out.dbcnt)
52851208Sgreen		(void)memmove(out.db, out.dbp - out.dbcnt, out.dbcnt);
5291556Srgrimes	out.dbp = out.db + out.dbcnt;
5301556Srgrimes}
531