xref: /openbsd-src/bin/dd/dd.c (revision f901c358bd03eff7abd75270f605497f5228c6b0)
1*f901c358Sderaadt /*	$OpenBSD: dd.c,v 1.29 2024/07/12 14:30:27 deraadt Exp $	*/
27f82c603Sniklas /*	$NetBSD: dd.c,v 1.6 1996/02/20 19:29:06 jtc Exp $	*/
3df930be7Sderaadt 
4df930be7Sderaadt /*-
5df930be7Sderaadt  * Copyright (c) 1991, 1993, 1994
6df930be7Sderaadt  *	The Regents of the University of California.  All rights reserved.
7df930be7Sderaadt  *
8df930be7Sderaadt  * This code is derived from software contributed to Berkeley by
9df930be7Sderaadt  * Keith Muller of the University of California, San Diego and Lance
10df930be7Sderaadt  * Visser of Convex Computer Corporation.
11df930be7Sderaadt  *
12df930be7Sderaadt  * Redistribution and use in source and binary forms, with or without
13df930be7Sderaadt  * modification, are permitted provided that the following conditions
14df930be7Sderaadt  * are met:
15df930be7Sderaadt  * 1. Redistributions of source code must retain the above copyright
16df930be7Sderaadt  *    notice, this list of conditions and the following disclaimer.
17df930be7Sderaadt  * 2. Redistributions in binary form must reproduce the above copyright
18df930be7Sderaadt  *    notice, this list of conditions and the following disclaimer in the
19df930be7Sderaadt  *    documentation and/or other materials provided with the distribution.
2029295d1cSmillert  * 3. Neither the name of the University nor the names of its contributors
21df930be7Sderaadt  *    may be used to endorse or promote products derived from this software
22df930be7Sderaadt  *    without specific prior written permission.
23df930be7Sderaadt  *
24df930be7Sderaadt  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25df930be7Sderaadt  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26df930be7Sderaadt  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27df930be7Sderaadt  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28df930be7Sderaadt  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29df930be7Sderaadt  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30df930be7Sderaadt  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31df930be7Sderaadt  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32df930be7Sderaadt  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33df930be7Sderaadt  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34df930be7Sderaadt  * SUCH DAMAGE.
35df930be7Sderaadt  */
36df930be7Sderaadt 
37b9fc9a72Sderaadt #include <sys/types.h>
38df930be7Sderaadt #include <sys/stat.h>
39df930be7Sderaadt #include <sys/ioctl.h>
40df930be7Sderaadt #include <sys/mtio.h>
41df930be7Sderaadt 
42df930be7Sderaadt #include <ctype.h>
43df930be7Sderaadt #include <err.h>
44df930be7Sderaadt #include <errno.h>
45df930be7Sderaadt #include <fcntl.h>
46df930be7Sderaadt #include <signal.h>
47df930be7Sderaadt #include <stdio.h>
48df930be7Sderaadt #include <stdlib.h>
49df930be7Sderaadt #include <string.h>
50df930be7Sderaadt #include <unistd.h>
51df930be7Sderaadt 
52df930be7Sderaadt #include "dd.h"
53df930be7Sderaadt #include "extern.h"
54df930be7Sderaadt 
55c72b5b24Smillert static void dd_close(void);
56c72b5b24Smillert static void dd_in(void);
57c72b5b24Smillert static void getfdtype(IO *);
58c72b5b24Smillert static void setup(void);
59df930be7Sderaadt 
60b9fc9a72Sderaadt #define MAXIMUM(a, b)	(((a) > (b)) ? (a) : (b))
61b9fc9a72Sderaadt 
62df930be7Sderaadt IO	in, out;		/* input/output state */
63df930be7Sderaadt STAT	st;			/* statistics */
64c72b5b24Smillert void	(*cfunc)(void);		/* conversion function */
65f6ff413cSmillert size_t	cpy_cnt;		/* # of blocks to copy */
66df930be7Sderaadt u_int	ddflags;		/* conversion options */
67f6ff413cSmillert size_t	cbsz;			/* conversion block size */
68f6ff413cSmillert size_t	files_cnt = 1;		/* # of files to copy */
697f82c603Sniklas const	u_char	*ctab;		/* conversion table */
70df930be7Sderaadt 
71df930be7Sderaadt int
main(int argc,char * argv[])7228416801Sderaadt main(int argc, char *argv[])
73df930be7Sderaadt {
74df930be7Sderaadt 	jcl(argv);
75df930be7Sderaadt 	setup();
76df930be7Sderaadt 
77*f901c358Sderaadt 	(void)signal(SIGINFO, sig_summary);
78*f901c358Sderaadt 	(void)signal(SIGINT, sig_terminate);
79df930be7Sderaadt 
80*f901c358Sderaadt 	atexit(exit_summary);
81df930be7Sderaadt 
829c9e0865Smillert 	if (cpy_cnt != (size_t)-1) {
83df930be7Sderaadt 		while (files_cnt--)
84df930be7Sderaadt 			dd_in();
851502d8fdSmillert 	}
86df930be7Sderaadt 
87df930be7Sderaadt 	dd_close();
88df930be7Sderaadt 	exit(0);
89df930be7Sderaadt }
90df930be7Sderaadt 
91df930be7Sderaadt static void
setup(void)9228416801Sderaadt setup(void)
93df930be7Sderaadt {
94df930be7Sderaadt 	if (in.name == NULL) {
95df930be7Sderaadt 		in.name = "stdin";
96df930be7Sderaadt 		in.fd = STDIN_FILENO;
97df930be7Sderaadt 	} else {
98b7041c07Sderaadt 		in.fd = open(in.name, O_RDONLY);
993aaa63ebSderaadt 		if (in.fd == -1)
100df930be7Sderaadt 			err(1, "%s", in.name);
101df930be7Sderaadt 	}
102df930be7Sderaadt 
103df930be7Sderaadt 	getfdtype(&in);
104df930be7Sderaadt 
105df930be7Sderaadt 	if (files_cnt > 1 && !(in.flags & ISTAPE))
106df930be7Sderaadt 		errx(1, "files is not supported for non-tape devices");
107df930be7Sderaadt 
108df930be7Sderaadt 	if (out.name == NULL) {
109df930be7Sderaadt 		/* No way to check for read access here. */
110df930be7Sderaadt 		out.fd = STDOUT_FILENO;
111df930be7Sderaadt 		out.name = "stdout";
112df930be7Sderaadt 	} else {
113df930be7Sderaadt #define	OFLAGS \
114df930be7Sderaadt     (O_CREAT | (ddflags & (C_SEEK | C_NOTRUNC) ? 0 : O_TRUNC))
115df930be7Sderaadt 		out.fd = open(out.name, O_RDWR | OFLAGS, DEFFILEMODE);
116df930be7Sderaadt 		/*
117df930be7Sderaadt 		 * May not have read access, so try again with write only.
118df930be7Sderaadt 		 * Without read we may have a problem if output also does
119df930be7Sderaadt 		 * not support seeks.
120df930be7Sderaadt 		 */
1213aaa63ebSderaadt 		if (out.fd == -1) {
122df930be7Sderaadt 			out.fd = open(out.name, O_WRONLY | OFLAGS, DEFFILEMODE);
123df930be7Sderaadt 			out.flags |= NOREAD;
124df930be7Sderaadt 		}
1253aaa63ebSderaadt 		if (out.fd == -1)
126df930be7Sderaadt 			err(1, "%s", out.name);
127df930be7Sderaadt 	}
128df930be7Sderaadt 
129df930be7Sderaadt 	getfdtype(&out);
130df930be7Sderaadt 
131df930be7Sderaadt 	/*
132df930be7Sderaadt 	 * Allocate space for the input and output buffers.  If not doing
133df930be7Sderaadt 	 * record oriented I/O, only need a single buffer.
134df930be7Sderaadt 	 */
135df930be7Sderaadt 	if (!(ddflags & (C_BLOCK|C_UNBLOCK))) {
136df930be7Sderaadt 		if ((in.db = malloc(out.dbsz + in.dbsz - 1)) == NULL)
137764064c4Smickey 			err(1, "input buffer");
138df930be7Sderaadt 		out.db = in.db;
1395a04faebScheloha 	} else {
1405a04faebScheloha 		in.db = malloc(MAXIMUM(in.dbsz, cbsz) + cbsz);
1415a04faebScheloha 		if (in.db == NULL)
1425a04faebScheloha 			err(1, "input buffer");
1435a04faebScheloha 		out.db = malloc(out.dbsz + cbsz);
1445a04faebScheloha 		if (out.db == NULL)
145764064c4Smickey 			err(1, "output buffer");
1465a04faebScheloha 	}
147df930be7Sderaadt 	in.dbp = in.db;
148df930be7Sderaadt 	out.dbp = out.db;
149df930be7Sderaadt 
150df930be7Sderaadt 	/* Position the input/output streams. */
151df930be7Sderaadt 	if (in.offset)
152df930be7Sderaadt 		pos_in();
153df930be7Sderaadt 	if (out.offset)
154df930be7Sderaadt 		pos_out();
155df930be7Sderaadt 
1560bd1216cSderaadt 	if (pledge("stdio", NULL) == -1)
1570bd1216cSderaadt 		err(1, "pledge");
1587d0369c1Sderaadt 
159df930be7Sderaadt 	/*
160df930be7Sderaadt 	 * Truncate the output file; ignore errors because it fails on some
161df930be7Sderaadt 	 * kinds of output files, tapes, for example.
162df930be7Sderaadt 	 */
163764064c4Smickey 	if ((ddflags & (C_OF | C_SEEK | C_NOTRUNC)) == (C_OF | C_SEEK))
164f6ff413cSmillert 		(void)ftruncate(out.fd, out.offset * out.dbsz);
165df930be7Sderaadt 
166df930be7Sderaadt 	/*
167df930be7Sderaadt 	 * If converting case at the same time as another conversion, build a
168df930be7Sderaadt 	 * table that does both at once.  If just converting case, use the
169df930be7Sderaadt 	 * built-in tables.
170df930be7Sderaadt 	 */
171df930be7Sderaadt 	if (ddflags & (C_LCASE|C_UCASE)) {
172df930be7Sderaadt #ifdef	NO_CONV
173df930be7Sderaadt 		/* Should not get here, but just in case... */
174df930be7Sderaadt 		errx(1, "case conv and -DNO_CONV");
175df930be7Sderaadt #else	/* NO_CONV */
176c29acd29Stedu 		u_int cnt;
1777f82c603Sniklas 		if (ddflags & C_ASCII || ddflags & C_EBCDIC) {
178df930be7Sderaadt 			if (ddflags & C_LCASE) {
179df930be7Sderaadt 				for (cnt = 0; cnt < 0377; ++cnt)
1807f82c603Sniklas 					casetab[cnt] = tolower(ctab[cnt]);
181df930be7Sderaadt 			} else {
182df930be7Sderaadt 				for (cnt = 0; cnt < 0377; ++cnt)
1837f82c603Sniklas 					casetab[cnt] = toupper(ctab[cnt]);
184df930be7Sderaadt 			}
1857f82c603Sniklas 		} else {
186df930be7Sderaadt 			if (ddflags & C_LCASE) {
187df930be7Sderaadt 				for (cnt = 0; cnt < 0377; ++cnt)
1887f82c603Sniklas 					casetab[cnt] = tolower(cnt);
189df930be7Sderaadt 			} else {
190df930be7Sderaadt 				for (cnt = 0; cnt < 0377; ++cnt)
1917f82c603Sniklas 					casetab[cnt] = toupper(cnt);
192df930be7Sderaadt 			}
1937f82c603Sniklas 		}
1947f82c603Sniklas 
1957f82c603Sniklas 		ctab = casetab;
196df930be7Sderaadt #endif	/* NO_CONV */
197df930be7Sderaadt 	}
198df930be7Sderaadt 
199e4cb6409Shugh 	/* Statistics timestamp. */
200c483b058Stedu 	clock_gettime(CLOCK_MONOTONIC, &st.start);
201df930be7Sderaadt }
202df930be7Sderaadt 
203df930be7Sderaadt static void
getfdtype(IO * io)20428416801Sderaadt getfdtype(IO *io)
205df930be7Sderaadt {
206df930be7Sderaadt 	struct mtget mt;
207df930be7Sderaadt 	struct stat sb;
208df930be7Sderaadt 
209df930be7Sderaadt 	if (fstat(io->fd, &sb))
210df930be7Sderaadt 		err(1, "%s", io->name);
211df930be7Sderaadt 	if (S_ISCHR(sb.st_mode))
212df930be7Sderaadt 		io->flags |= ioctl(io->fd, MTIOCGET, &mt) ? ISCHR : ISTAPE;
2138cead232Smillert 	if (S_ISFIFO(sb.st_mode) || S_ISSOCK(sb.st_mode))
2148cead232Smillert 		io->flags |= ISPIPE;
215df930be7Sderaadt }
216df930be7Sderaadt 
217df930be7Sderaadt static void
swapbytes(void * v,size_t len)218a9f9340dStedu swapbytes(void *v, size_t len)
219a9f9340dStedu {
220a9f9340dStedu 	unsigned char *p = v;
221a9f9340dStedu 	unsigned char t;
222a9f9340dStedu 
223a9f9340dStedu 	while (len > 1) {
224a9f9340dStedu 		t = p[0];
225a9f9340dStedu 		p[0] = p[1];
226a9f9340dStedu 		p[1] = t;
227a9f9340dStedu 		p += 2;
228a9f9340dStedu 		len -= 2;
229a9f9340dStedu 	}
230a9f9340dStedu }
231a9f9340dStedu 
232a9f9340dStedu 
233a9f9340dStedu static void
dd_in(void)23428416801Sderaadt dd_in(void)
235df930be7Sderaadt {
236f6ff413cSmillert 	ssize_t n;
237df930be7Sderaadt 
238e6181295Sderaadt 	for (;;) {
239df930be7Sderaadt 		if (cpy_cnt && (st.in_full + st.in_part) >= cpy_cnt)
240df930be7Sderaadt 			return;
241df930be7Sderaadt 
242df930be7Sderaadt 		/*
243e6181295Sderaadt 		 * Zero the buffer first if sync; if doing block operations
244df930be7Sderaadt 		 * use spaces.
245df930be7Sderaadt 		 */
246c844cb3dSderaadt 		if (ddflags & C_SYNC) {
247e6181295Sderaadt 			if (ddflags & (C_BLOCK|C_UNBLOCK))
248f6ff413cSmillert 				(void)memset(in.dbp, ' ', in.dbsz);
249df930be7Sderaadt 			else
250f6ff413cSmillert 				(void)memset(in.dbp, 0, in.dbsz);
251c844cb3dSderaadt 		}
252df930be7Sderaadt 
253df930be7Sderaadt 		n = read(in.fd, in.dbp, in.dbsz);
254df930be7Sderaadt 		if (n == 0) {
255df930be7Sderaadt 			in.dbrcnt = 0;
256df930be7Sderaadt 			return;
257df930be7Sderaadt 		}
258df930be7Sderaadt 
259df930be7Sderaadt 		/* Read error. */
2603aaa63ebSderaadt 		if (n == -1) {
261df930be7Sderaadt 			/*
262df930be7Sderaadt 			 * If noerror not specified, die.  POSIX requires that
263df930be7Sderaadt 			 * the warning message be followed by an I/O display.
264df930be7Sderaadt 			 */
265e6181295Sderaadt 			if (!(ddflags & C_NOERROR))
266df930be7Sderaadt 				err(1, "%s", in.name);
267df930be7Sderaadt 			warn("%s", in.name);
268*f901c358Sderaadt 			sig_summary(0);
269df930be7Sderaadt 
270df930be7Sderaadt 			/*
271df930be7Sderaadt 			 * If it's not a tape drive or a pipe, seek past the
272df930be7Sderaadt 			 * error.  If your OS doesn't do the right thing for
273df930be7Sderaadt 			 * raw disks this section should be modified to re-read
274df930be7Sderaadt 			 * in sector size chunks.
275df930be7Sderaadt 			 */
276df930be7Sderaadt 			if (!(in.flags & (ISPIPE|ISTAPE)) &&
277df930be7Sderaadt 			    lseek(in.fd, (off_t)in.dbsz, SEEK_CUR))
278df930be7Sderaadt 				warn("%s", in.name);
279df930be7Sderaadt 
280df930be7Sderaadt 			/* If sync not specified, omit block and continue. */
281df930be7Sderaadt 			if (!(ddflags & C_SYNC))
282df930be7Sderaadt 				continue;
283df930be7Sderaadt 
284df930be7Sderaadt 			/* Read errors count as full blocks. */
285df930be7Sderaadt 			in.dbcnt += in.dbrcnt = in.dbsz;
286df930be7Sderaadt 			++st.in_full;
287df930be7Sderaadt 
288df930be7Sderaadt 		/* Handle full input blocks. */
289df930be7Sderaadt 		} else if (n == in.dbsz) {
290df930be7Sderaadt 			in.dbcnt += in.dbrcnt = n;
291df930be7Sderaadt 			++st.in_full;
292df930be7Sderaadt 
293df930be7Sderaadt 		/* Handle partial input blocks. */
294df930be7Sderaadt 		} else {
295df930be7Sderaadt 			/* If sync, use the entire block. */
296df930be7Sderaadt 			if (ddflags & C_SYNC)
297df930be7Sderaadt 				in.dbcnt += in.dbrcnt = in.dbsz;
298df930be7Sderaadt 			else
299df930be7Sderaadt 				in.dbcnt += in.dbrcnt = n;
300df930be7Sderaadt 			++st.in_part;
301df930be7Sderaadt 		}
302df930be7Sderaadt 
303df930be7Sderaadt 		/*
304df930be7Sderaadt 		 * POSIX states that if bs is set and no other conversions
305df930be7Sderaadt 		 * than noerror, notrunc or sync are specified, the block
306df930be7Sderaadt 		 * is output without buffering as it is read.
307df930be7Sderaadt 		 */
308df930be7Sderaadt 		if (ddflags & C_BS) {
309df930be7Sderaadt 			out.dbcnt = in.dbcnt;
310df930be7Sderaadt 			dd_out(1);
311df930be7Sderaadt 			in.dbcnt = 0;
312df930be7Sderaadt 			continue;
313df930be7Sderaadt 		}
314df930be7Sderaadt 
315df930be7Sderaadt 		if (ddflags & C_SWAB) {
316b39ae59aStedu 			if ((n = in.dbrcnt) & 1) {
317df930be7Sderaadt 				++st.swab;
318df930be7Sderaadt 				--n;
319df930be7Sderaadt 			}
320a9f9340dStedu 			swapbytes(in.dbp, n);
321df930be7Sderaadt 		}
322df930be7Sderaadt 
323df930be7Sderaadt 		in.dbp += in.dbrcnt;
324df930be7Sderaadt 		(*cfunc)();
325df930be7Sderaadt 	}
326df930be7Sderaadt }
327df930be7Sderaadt 
328df930be7Sderaadt /*
32959f89586Stodd  * Cleanup any remaining I/O and flush output.  If necessary, output file
330df930be7Sderaadt  * is truncated.
331df930be7Sderaadt  */
332df930be7Sderaadt static void
dd_close(void)33328416801Sderaadt dd_close(void)
334df930be7Sderaadt {
335df930be7Sderaadt 	if (cfunc == def)
336df930be7Sderaadt 		def_close();
337df930be7Sderaadt 	else if (cfunc == block)
338df930be7Sderaadt 		block_close();
339df930be7Sderaadt 	else if (cfunc == unblock)
340df930be7Sderaadt 		unblock_close();
341e6181295Sderaadt 	if (ddflags & C_OSYNC && out.dbcnt && out.dbcnt < out.dbsz) {
342e6181295Sderaadt 		if (ddflags & (C_BLOCK|C_UNBLOCK))
343e6181295Sderaadt 			memset(out.dbp, ' ', out.dbsz - out.dbcnt);
344e6181295Sderaadt 		else
345e6181295Sderaadt 			memset(out.dbp, 0, out.dbsz - out.dbcnt);
346df930be7Sderaadt 		out.dbcnt = out.dbsz;
347df930be7Sderaadt 	}
348df930be7Sderaadt 	if (out.dbcnt)
349df930be7Sderaadt 		dd_out(1);
350bce29b5cSbluhm 	if (ddflags & C_FSYNC) {
351bce29b5cSbluhm 		if (fsync(out.fd) == -1)
352bce29b5cSbluhm 			err(1, "fsync %s", out.name);
353bce29b5cSbluhm 	}
354df930be7Sderaadt }
355df930be7Sderaadt 
356df930be7Sderaadt void
dd_out(int force)35728416801Sderaadt dd_out(int force)
358df930be7Sderaadt {
359df930be7Sderaadt 	static int warned;
360f6ff413cSmillert 	size_t cnt, n;
361f6ff413cSmillert 	ssize_t nw;
362df930be7Sderaadt 	u_char *outp;
363df930be7Sderaadt 
364df930be7Sderaadt 	/*
365df930be7Sderaadt 	 * Write one or more blocks out.  The common case is writing a full
366df930be7Sderaadt 	 * output block in a single write; increment the full block stats.
367df930be7Sderaadt 	 * Otherwise, we're into partial block writes.  If a partial write,
368df930be7Sderaadt 	 * and it's a character device, just warn.  If a tape device, quit.
369df930be7Sderaadt 	 *
370df930be7Sderaadt 	 * The partial writes represent two cases.  1: Where the input block
371df930be7Sderaadt 	 * was less than expected so the output block was less than expected.
372df930be7Sderaadt 	 * 2: Where the input block was the right size but we were forced to
373df930be7Sderaadt 	 * write the block in multiple chunks.  The original versions of dd(1)
374df930be7Sderaadt 	 * never wrote a block in more than a single write, so the latter case
375df930be7Sderaadt 	 * never happened.
376df930be7Sderaadt 	 *
377df930be7Sderaadt 	 * One special case is if we're forced to do the write -- in that case
378df930be7Sderaadt 	 * we play games with the buffer size, and it's usually a partial write.
379df930be7Sderaadt 	 */
380df930be7Sderaadt 	outp = out.db;
381df930be7Sderaadt 	for (n = force ? out.dbcnt : out.dbsz;; n = out.dbsz) {
382df930be7Sderaadt 		for (cnt = n;; cnt -= nw) {
383df930be7Sderaadt 			nw = write(out.fd, outp, cnt);
384df930be7Sderaadt 			if (nw == 0)
385df930be7Sderaadt 				errx(1, "%s: end of device", out.name);
3863aaa63ebSderaadt 			if (nw == -1) {
387df930be7Sderaadt 				if (errno != EINTR)
388df930be7Sderaadt 					err(1, "%s", out.name);
389df930be7Sderaadt 				nw = 0;
390df930be7Sderaadt 			}
391df930be7Sderaadt 			outp += nw;
392df930be7Sderaadt 			st.bytes += nw;
393df930be7Sderaadt 			if (nw == n) {
394df930be7Sderaadt 				if (n != out.dbsz)
395df930be7Sderaadt 					++st.out_part;
396df930be7Sderaadt 				else
397df930be7Sderaadt 					++st.out_full;
398df930be7Sderaadt 				break;
399df930be7Sderaadt 			}
400df930be7Sderaadt 			++st.out_part;
401df930be7Sderaadt 			if (nw == cnt)
402df930be7Sderaadt 				break;
403df930be7Sderaadt 			if (out.flags & ISCHR && !warned) {
404df930be7Sderaadt 				warned = 1;
405df930be7Sderaadt 				warnx("%s: short write on character device",
406df930be7Sderaadt 				    out.name);
407df930be7Sderaadt 			}
408df930be7Sderaadt 			if (out.flags & ISTAPE)
409df930be7Sderaadt 				errx(1, "%s: short write on tape device", out.name);
410df930be7Sderaadt 		}
411df930be7Sderaadt 		if ((out.dbcnt -= n) < out.dbsz)
412df930be7Sderaadt 			break;
413df930be7Sderaadt 	}
414df930be7Sderaadt 
415df930be7Sderaadt 	/* Reassemble the output block. */
416df930be7Sderaadt 	if (out.dbcnt)
417f6ff413cSmillert 		(void)memmove(out.db, out.dbp - out.dbcnt, out.dbcnt);
418df930be7Sderaadt 	out.dbp = out.db + out.dbcnt;
419df930be7Sderaadt }
420