1 /*        $NetBSD: dd.c,v 1.53 2019/10/04 08:57:37 mrg Exp $          */
2 
3 /*-
4  * Copyright (c) 1991, 1993, 1994
5  *        The Regents of the University of California.  All rights reserved.
6  *
7  * This code is derived from software contributed to Berkeley by
8  * Keith Muller of the University of California, San Diego and Lance
9  * Visser of Convex Computer Corporation.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  * 3. Neither the name of the University nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35 
36 #include <sys/cdefs.h>
37 #ifndef lint
38 __COPYRIGHT("@(#) Copyright (c) 1991, 1993, 1994\
39  The Regents of the University of California.  All rights reserved.");
40 #endif /* not lint */
41 
42 #ifndef lint
43 #if 0
44 static char sccsid[] = "@(#)dd.c        8.5 (Berkeley) 4/2/94";
45 #else
46 __RCSID("$NetBSD: dd.c,v 1.53 2019/10/04 08:57:37 mrg Exp $");
47 #endif
48 #endif /* not lint */
49 
50 #include <sys/param.h>
51 #include <sys/stat.h>
52 #include <sys/ioctl.h>
53 #include <sys/mtio.h>
54 #include <sys/time.h>
55 
56 #include <ctype.h>
57 #include <err.h>
58 #include <errno.h>
59 #include <fcntl.h>
60 #include <locale.h>
61 #include <signal.h>
62 #include <stdio.h>
63 #include <stdlib.h>
64 #include <string.h>
65 #include <unistd.h>
66 
67 #include "dd.h"
68 #include "extern.h"
69 
70 static void dd_close(void);
71 static void dd_in(void);
72 static void getfdtype(IO *);
73 static void redup_clean_fd(IO *);
74 static void setup(void);
75 static void *buffer_alloc(size_t);
76 
77 IO                  in, out;            /* input/output state */
78 STAT                st;                           /* statistics */
79 void                (*cfunc)(void);               /* conversion function */
80 uint64_t  cpy_cnt;            /* # of blocks to copy */
81 static off_t        pending = 0;                  /* pending seek if sparse */
82 u_int               ddflags;            /* conversion options */
83 #ifdef NO_IOFLAG
84 #define             iflag     O_RDONLY
85 #define             oflag     O_CREAT
86 #else
87 u_int               iflag = O_RDONLY;   /* open(2) flags for input file */
88 u_int               oflag = O_CREAT;    /* open(2) flags for output file */
89 #endif /* NO_IOFLAG */
90 uint64_t  cbsz;                         /* conversion block size */
91 u_int               files_cnt = 1;                /* # of files to copy */
92 uint64_t  progress = 0;                 /* display sign of life */
93 const u_char        *ctab;                        /* conversion table */
94 sigset_t  infoset;            /* a set blocking SIGINFO */
95 const char          *msgfmt = "posix";  /* default summary() message format */
96 
97 /*
98  * Ops for stdin/stdout and crunch'd dd.  These are always host ops.
99  */
100 static const struct ddfops ddfops_stdfd = {
101           .op_open = open,
102           .op_close = close,
103           .op_fcntl = fcntl,
104           .op_ioctl = ioctl,
105           .op_fstat = fstat,
106           .op_fsync = fsync,
107           .op_ftruncate = ftruncate,
108           .op_lseek = lseek,
109           .op_read = read,
110           .op_write = write,
111 };
112 extern const struct ddfops ddfops_prog;
113 
114 int
main(int argc,char * argv[])115 main(int argc, char *argv[])
116 {
117           int ch;
118 
119           setprogname(argv[0]);
120           (void)setlocale(LC_ALL, "");
121 
122           while ((ch = getopt(argc, argv, "")) != -1) {
123                     switch (ch) {
124                     default:
125                               errx(EXIT_FAILURE, "usage: dd [operand ...]");
126                               /* NOTREACHED */
127                     }
128           }
129           argc -= (optind - 1);
130           argv += (optind - 1);
131 
132           jcl(argv);
133 #ifndef CRUNCHOPS
134           if (ddfops_prog.op_init && ddfops_prog.op_init() == -1)
135                     err(1, "prog init");
136 #endif
137           setup();
138 
139           (void)signal(SIGINFO, summaryx);
140           (void)signal(SIGINT, terminate);
141           (void)sigemptyset(&infoset);
142           (void)sigaddset(&infoset, SIGINFO);
143 
144           (void)atexit(summary);
145 
146           while (files_cnt--)
147                     dd_in();
148 
149           dd_close();
150           exit(0);
151           /* NOTREACHED */
152 }
153 
154 static void *
buffer_alloc(size_t sz)155 buffer_alloc(size_t sz)
156 {
157           size_t align = getpagesize();
158           void *res;
159 
160           if (sz < align)
161                     res = malloc(sz);
162           else if (posix_memalign(&res, align, sz) != 0)
163                     res = NULL;
164 
165           return res;
166 }
167 
168 static void
setup(void)169 setup(void)
170 {
171 #ifdef CRUNCHOPS
172           const struct ddfops *prog_ops = &ddfops_stdfd;
173 #else
174           const struct ddfops *prog_ops = &ddfops_prog;
175 #endif
176 
177           if (in.name == NULL) {
178                     in.name = "stdin";
179                     in.fd = STDIN_FILENO;
180                     in.ops = &ddfops_stdfd;
181           } else {
182                     in.ops = prog_ops;
183                     in.fd = ddop_open(in, in.name, iflag, 0);
184                     if (in.fd < 0)
185                               err(EXIT_FAILURE, "%s", in.name);
186                               /* NOTREACHED */
187 
188                     /* Ensure in.fd is outside the stdio descriptor range */
189                     redup_clean_fd(&in);
190           }
191 
192           getfdtype(&in);
193 
194           if (files_cnt > 1 && !(in.flags & ISTAPE)) {
195                     errx(EXIT_FAILURE, "files is not supported for non-tape devices");
196                     /* NOTREACHED */
197           }
198 
199           if (out.name == NULL) {
200                     /* No way to check for read access here. */
201                     out.fd = STDOUT_FILENO;
202                     out.name = "stdout";
203                     out.ops = &ddfops_stdfd;
204           } else {
205                     out.ops = prog_ops;
206 
207 #ifndef NO_IOFLAG
208                     if ((oflag & O_TRUNC) && (ddflags & C_SEEK)) {
209                               errx(EXIT_FAILURE, "oflag=trunc is incompatible "
210                                    "with seek or oseek operands, giving up.");
211                               /* NOTREACHED */
212                     }
213                     if ((oflag & O_TRUNC) && (ddflags & C_NOTRUNC)) {
214                               errx(EXIT_FAILURE, "oflag=trunc is incompatible "
215                                    "with conv=notrunc operand, giving up.");
216                               /* NOTREACHED */
217                     }
218 #endif /* NO_IOFLAG */
219 #define   OFLAGS \
220     (oflag | (ddflags & (C_SEEK | C_NOTRUNC) ? 0 : O_TRUNC))
221                     out.fd = ddop_open(out, out.name, O_RDWR | OFLAGS, DEFFILEMODE);
222                     /*
223                      * May not have read access, so try again with write only.
224                      * Without read we may have a problem if output also does
225                      * not support seeks.
226                      */
227                     if (out.fd < 0) {
228                               out.fd = ddop_open(out, out.name, O_WRONLY | OFLAGS,
229                                   DEFFILEMODE);
230                               out.flags |= NOREAD;
231                     }
232                     if (out.fd < 0) {
233                               err(EXIT_FAILURE, "%s", out.name);
234                               /* NOTREACHED */
235                     }
236 
237                     /* Ensure out.fd is outside the stdio descriptor range */
238                     redup_clean_fd(&out);
239           }
240 
241           getfdtype(&out);
242 
243           /*
244            * Allocate space for the input and output buffers.  If not doing
245            * record oriented I/O, only need a single buffer.
246            */
247           if (!(ddflags & (C_BLOCK|C_UNBLOCK))) {
248                     size_t dbsz = out.dbsz;
249                     if (!(ddflags & C_BS))
250                               dbsz += in.dbsz - 1;
251                     if ((in.db = buffer_alloc(dbsz)) == NULL) {
252                               err(EXIT_FAILURE, NULL);
253                               /* NOTREACHED */
254                     }
255                     out.db = in.db;
256           } else if ((in.db =
257               buffer_alloc((u_int)(MAX(in.dbsz, cbsz) + cbsz))) == NULL ||
258               (out.db = buffer_alloc((u_int)(out.dbsz + cbsz))) == NULL) {
259                     err(EXIT_FAILURE, NULL);
260                     /* NOTREACHED */
261           }
262           in.dbp = in.db;
263           out.dbp = out.db;
264 
265           /* Position the input/output streams. */
266           if (in.offset)
267                     pos_in();
268           if (out.offset)
269                     pos_out();
270 
271           /*
272            * Truncate the output file; ignore errors because it fails on some
273            * kinds of output files, tapes, for example.
274            */
275           if ((ddflags & (C_OF | C_SEEK | C_NOTRUNC)) == (C_OF | C_SEEK))
276                     (void)ddop_ftruncate(out, out.fd, (off_t)out.offset * out.dbsz);
277 
278           /*
279            * If converting case at the same time as another conversion, build a
280            * table that does both at once.  If just converting case, use the
281            * built-in tables.
282            */
283           if (ddflags & (C_LCASE|C_UCASE)) {
284 #ifdef    NO_CONV
285                     /* Should not get here, but just in case... */
286                     errx(EXIT_FAILURE, "case conv and -DNO_CONV");
287                     /* NOTREACHED */
288 #else     /* NO_CONV */
289                     u_int cnt;
290 
291                     if (ddflags & C_ASCII || ddflags & C_EBCDIC) {
292                               if (ddflags & C_LCASE) {
293                                         for (cnt = 0; cnt < 256; ++cnt)
294                                                   casetab[cnt] = tolower(ctab[cnt]);
295                               } else {
296                                         for (cnt = 0; cnt < 256; ++cnt)
297                                                   casetab[cnt] = toupper(ctab[cnt]);
298                               }
299                     } else {
300                               if (ddflags & C_LCASE) {
301                                         for (cnt = 0; cnt < 256; ++cnt)
302                                                   casetab[cnt] = tolower(cnt);
303                               } else {
304                                         for (cnt = 0; cnt < 256; ++cnt)
305                                                   casetab[cnt] = toupper(cnt);
306                               }
307                     }
308 
309                     ctab = casetab;
310 #endif    /* NO_CONV */
311           }
312 
313           (void)gettimeofday(&st.start, NULL);    /* Statistics timestamp. */
314 }
315 
316 static void
getfdtype(IO * io)317 getfdtype(IO *io)
318 {
319           struct mtget mt;
320           struct stat sb;
321 
322           if (io->ops->op_fstat(io->fd, &sb)) {
323                     err(EXIT_FAILURE, "%s", io->name);
324                     /* NOTREACHED */
325           }
326           if (S_ISCHR(sb.st_mode))
327                     io->flags |= io->ops->op_ioctl(io->fd, MTIOCGET, &mt)
328                         ? ISCHR : ISTAPE;
329           else if (io->ops->op_lseek(io->fd, (off_t)0, SEEK_CUR) == -1
330               && errno == ESPIPE)
331                     io->flags |= ISPIPE;                    /* XXX fixed in 4.4BSD */
332 }
333 
334 /*
335  * Move the parameter file descriptor to a descriptor that is outside the
336  * stdio descriptor range, if necessary.  This is required to avoid
337  * accidentally outputting completion or error messages into the
338  * output file that were intended for the tty.
339  */
340 static void
redup_clean_fd(IO * io)341 redup_clean_fd(IO *io)
342 {
343           int fd = io->fd;
344           int newfd;
345 
346           if (fd != STDIN_FILENO && fd != STDOUT_FILENO &&
347               fd != STDERR_FILENO)
348                     /* File descriptor is ok, return immediately. */
349                     return;
350 
351           /*
352            * 3 is the first descriptor greater than STD*_FILENO.  Any
353            * free descriptor valued 3 or above is acceptable...
354            */
355           newfd = io->ops->op_fcntl(fd, F_DUPFD, 3);
356           if (newfd < 0) {
357                     err(EXIT_FAILURE, "dupfd IO");
358                     /* NOTREACHED */
359           }
360 
361           io->ops->op_close(fd);
362           io->fd = newfd;
363 }
364 
365 static void
dd_in(void)366 dd_in(void)
367 {
368           int flags;
369           int64_t n;
370 
371           for (flags = ddflags;;) {
372                     if (cpy_cnt && (st.in_full + st.in_part) >= cpy_cnt)
373                               return;
374 
375                     /*
376                      * Clear the buffer first if doing "sync" on input.
377                      * If doing block operations use spaces.  This will
378                      * affect not only the C_NOERROR case, but also the
379                      * last partial input block which should be padded
380                      * with zero and not garbage.
381                      */
382                     if (flags & C_SYNC) {
383                               if (flags & (C_BLOCK|C_UNBLOCK))
384                                         (void)memset(in.dbp, ' ', in.dbsz);
385                               else
386                                         (void)memset(in.dbp, 0, in.dbsz);
387                     }
388 
389                     n = ddop_read(in, in.fd, in.dbp, in.dbsz);
390                     if (n == 0) {
391                               in.dbrcnt = 0;
392                               return;
393                     }
394 
395                     /* Read error. */
396                     if (n < 0) {
397 
398                               /*
399                                * If noerror not specified, die.  POSIX requires that
400                                * the warning message be followed by an I/O display.
401                                */
402                               if (!(flags & C_NOERROR)) {
403                                         err(EXIT_FAILURE, "%s", in.name);
404                                         /* NOTREACHED */
405                               }
406                               warn("%s", in.name);
407                               summary();
408 
409                               /*
410                                * If it's not a tape drive or a pipe, seek past the
411                                * error.  If your OS doesn't do the right thing for
412                                * raw disks this section should be modified to re-read
413                                * in sector size chunks.
414                                */
415                               if (!(in.flags & (ISPIPE|ISTAPE)) &&
416                                   ddop_lseek(in, in.fd, (off_t)in.dbsz, SEEK_CUR))
417                                         warn("%s", in.name);
418 
419                               /* If sync not specified, omit block and continue. */
420                               if (!(ddflags & C_SYNC))
421                                         continue;
422 
423                               /* Read errors count as full blocks. */
424                               in.dbcnt += in.dbrcnt = in.dbsz;
425                               ++st.in_full;
426 
427                     /* Handle full input blocks. */
428                     } else if ((uint64_t)n == in.dbsz) {
429                               in.dbcnt += in.dbrcnt = n;
430                               ++st.in_full;
431 
432                     /* Handle partial input blocks. */
433                     } else {
434                               /* If sync, use the entire block. */
435                               if (ddflags & C_SYNC)
436                                         in.dbcnt += in.dbrcnt = in.dbsz;
437                               else
438                                         in.dbcnt += in.dbrcnt = n;
439                               ++st.in_part;
440                     }
441 
442                     /*
443                      * POSIX states that if bs is set and no other conversions
444                      * than noerror, notrunc or sync are specified, the block
445                      * is output without buffering as it is read.
446                      */
447                     if (ddflags & C_BS) {
448                               out.dbcnt = in.dbcnt;
449                               dd_out(1);
450                               in.dbcnt = 0;
451                               continue;
452                     }
453 
454                     if (ddflags & C_SWAB) {
455                               if ((n = in.dbrcnt) & 1) {
456                                         ++st.swab;
457                                         --n;
458                               }
459                               dd_swab(in.dbp, in.dbp, n);
460                     }
461 
462                     in.dbp += in.dbrcnt;
463                     (*cfunc)();
464           }
465 }
466 
467 /*
468  * Cleanup any remaining I/O and flush output.  If necessary, output file
469  * is truncated.
470  */
471 static void
dd_close(void)472 dd_close(void)
473 {
474 
475           if (cfunc == def)
476                     def_close();
477           else if (cfunc == block)
478                     block_close();
479           else if (cfunc == unblock)
480                     unblock_close();
481           if (ddflags & C_OSYNC && out.dbcnt < out.dbsz) {
482                     (void)memset(out.dbp, 0, out.dbsz - out.dbcnt);
483                     out.dbcnt = out.dbsz;
484           }
485           /* If there are pending sparse blocks, make sure
486            * to write out the final block un-sparse
487            */
488           if ((out.dbcnt == 0) && pending) {
489                     memset(out.db, 0, out.dbsz);
490                     out.dbcnt = out.dbsz;
491                     out.dbp = out.db + out.dbcnt;
492                     pending -= out.dbsz;
493           }
494           if (out.dbcnt)
495                     dd_out(1);
496 
497           /*
498            * Reporting nfs write error may be deferred until next
499            * write(2) or close(2) system call.  So, we need to do an
500            * extra check.  If an output is stdout, the file structure
501            * may be shared with other processes and close(2) just
502            * decreases the reference count.
503            */
504           if (out.fd == STDOUT_FILENO && ddop_fsync(out, out.fd) == -1
505               && errno != EINVAL) {
506                     err(EXIT_FAILURE, "fsync stdout");
507                     /* NOTREACHED */
508           }
509           if (ddop_close(out, out.fd) == -1) {
510                     err(EXIT_FAILURE, "close");
511                     /* NOTREACHED */
512           }
513 }
514 
515 void
dd_out(int force)516 dd_out(int force)
517 {
518           static int warned;
519           int64_t cnt, n, nw;
520           u_char *outp;
521 
522           /*
523            * Write one or more blocks out.  The common case is writing a full
524            * output block in a single write; increment the full block stats.
525            * Otherwise, we're into partial block writes.  If a partial write,
526            * and it's a character device, just warn.  If a tape device, quit.
527            *
528            * The partial writes represent two cases.  1: Where the input block
529            * was less than expected so the output block was less than expected.
530            * 2: Where the input block was the right size but we were forced to
531            * write the block in multiple chunks.  The original versions of dd(1)
532            * never wrote a block in more than a single write, so the latter case
533            * never happened.
534            *
535            * One special case is if we're forced to do the write -- in that case
536            * we play games with the buffer size, and it's usually a partial write.
537            */
538           outp = out.db;
539           for (n = force ? out.dbcnt : out.dbsz;; n = out.dbsz) {
540                     for (cnt = n;; cnt -= nw) {
541 
542                               if (!force && ddflags & C_SPARSE) {
543                                         int sparse, i;
544                                         sparse = 1;         /* Is buffer sparse? */
545                                         for (i = 0; i < cnt; i++)
546                                                   if (outp[i] != 0) {
547                                                             sparse = 0;
548                                                             break;
549                                                   }
550                                         if (sparse) {
551                                                   pending += cnt;
552                                                   outp += cnt;
553                                                   nw = 0;
554                                                   break;
555                                         }
556                               }
557                               if (pending != 0) {
558                                         if (ddop_lseek(out,
559                                             out.fd, pending, SEEK_CUR) == -1)
560                                                   err(EXIT_FAILURE, "%s: seek error creating sparse file",
561                                                       out.name);
562                               }
563                               nw = bwrite(&out, outp, cnt);
564                               if (nw <= 0) {
565                                         if (nw == 0)
566                                                   errx(EXIT_FAILURE,
567                                                             "%s: end of device", out.name);
568                                                   /* NOTREACHED */
569                                         if (errno != EINTR)
570                                                   err(EXIT_FAILURE, "%s", out.name);
571                                                   /* NOTREACHED */
572                                         nw = 0;
573                               }
574                               if (pending) {
575                                         st.bytes += pending;
576                                         st.sparse += pending/out.dbsz;
577                                         st.out_full += pending/out.dbsz;
578                                         pending = 0;
579                               }
580                               outp += nw;
581                               st.bytes += nw;
582                               if (nw == n) {
583                                         if ((uint64_t)n != out.dbsz)
584                                                   ++st.out_part;
585                                         else
586                                                   ++st.out_full;
587                                         break;
588                               }
589                               ++st.out_part;
590                               if (nw == cnt)
591                                         break;
592                               if (out.flags & ISCHR && !warned) {
593                                         warned = 1;
594                                         warnx("%s: short write on character device", out.name);
595                               }
596                               if (out.flags & ISTAPE)
597                                         errx(EXIT_FAILURE,
598                                                   "%s: short write on tape device", out.name);
599                                         /* NOTREACHED */
600 
601                     }
602                     if ((out.dbcnt -= n) < out.dbsz)
603                               break;
604           }
605 
606           /* Reassemble the output block. */
607           if (out.dbcnt)
608                     (void)memmove(out.db, out.dbp - out.dbcnt, out.dbcnt);
609           out.dbp = out.db + out.dbcnt;
610 
611           if (progress && (st.out_full + st.out_part) % progress == 0)
612                     (void)write(STDERR_FILENO, ".", 1);
613 }
614 
615 /*
616  * A protected against SIGINFO write
617  */
618 ssize_t
bwrite(IO * io,const void * buf,size_t len)619 bwrite(IO *io, const void *buf, size_t len)
620 {
621           sigset_t oset;
622           ssize_t rv;
623           int oerrno;
624 
625           (void)sigprocmask(SIG_BLOCK, &infoset, &oset);
626           rv = io->ops->op_write(io->fd, buf, len);
627           oerrno = errno;
628           (void)sigprocmask(SIG_SETMASK, &oset, NULL);
629           errno = oerrno;
630           return (rv);
631 }
632