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