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