dd.c revision 19720
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 * 3. All advertising materials mentioning features or use of this software 181556Srgrimes * must display the following acknowledgement: 191556Srgrimes * This product includes software developed by the University of 201556Srgrimes * California, Berkeley and its contributors. 211556Srgrimes * 4. Neither the name of the University nor the names of its contributors 221556Srgrimes * may be used to endorse or promote products derived from this software 231556Srgrimes * without specific prior written permission. 241556Srgrimes * 251556Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 261556Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 271556Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 281556Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 291556Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 301556Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 311556Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 321556Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 331556Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 341556Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 351556Srgrimes * SUCH DAMAGE. 363044Sdg * 3719720Sphk * $Id: dd.c,v 1.6 1996/11/12 23:09:09 phk Exp $ 381556Srgrimes */ 391556Srgrimes 401556Srgrimes#ifndef lint 411556Srgrimesstatic char copyright[] = 421556Srgrimes"@(#) Copyright (c) 1991, 1993, 1994\n\ 431556Srgrimes The Regents of the University of California. All rights reserved.\n"; 441556Srgrimes#endif /* not lint */ 451556Srgrimes 461556Srgrimes#ifndef lint 471556Srgrimesstatic char sccsid[] = "@(#)dd.c 8.5 (Berkeley) 4/2/94"; 481556Srgrimes#endif /* not lint */ 491556Srgrimes 501556Srgrimes#include <sys/param.h> 511556Srgrimes#include <sys/stat.h> 521556Srgrimes#include <sys/ioctl.h> 531556Srgrimes#include <sys/mtio.h> 541556Srgrimes 551556Srgrimes#include <ctype.h> 561556Srgrimes#include <err.h> 571556Srgrimes#include <errno.h> 581556Srgrimes#include <fcntl.h> 5919720Sphk#include <locale.h> 601556Srgrimes#include <signal.h> 611556Srgrimes#include <stdio.h> 621556Srgrimes#include <stdlib.h> 631556Srgrimes#include <string.h> 641556Srgrimes#include <unistd.h> 651556Srgrimes 661556Srgrimes#include "dd.h" 671556Srgrimes#include "extern.h" 681556Srgrimes 691556Srgrimesstatic void dd_close __P((void)); 701556Srgrimesstatic void dd_in __P((void)); 711556Srgrimesstatic void getfdtype __P((IO *)); 721556Srgrimesstatic void setup __P((void)); 731556Srgrimes 741556SrgrimesIO in, out; /* input/output state */ 751556SrgrimesSTAT st; /* statistics */ 761556Srgrimesvoid (*cfunc) __P((void)); /* conversion function */ 771556Srgrimesu_long cpy_cnt; /* # of blocks to copy */ 781556Srgrimesu_int ddflags; /* conversion options */ 791556Srgrimesu_int cbsz; /* conversion block size */ 801556Srgrimesu_int files_cnt = 1; /* # of files to copy */ 811556Srgrimesu_char *ctab; /* conversion table */ 821556Srgrimes 831556Srgrimesint 841556Srgrimesmain(argc, argv) 851556Srgrimes int argc; 861556Srgrimes char *argv[]; 871556Srgrimes{ 8819720Sphk (void)setlocale(LC_CTYPE, ""); 891556Srgrimes jcl(argv); 901556Srgrimes setup(); 911556Srgrimes 921556Srgrimes (void)signal(SIGINFO, summaryx); 931556Srgrimes (void)signal(SIGINT, terminate); 941556Srgrimes 951556Srgrimes atexit(summary); 961556Srgrimes 971556Srgrimes while (files_cnt--) 981556Srgrimes dd_in(); 991556Srgrimes 1001556Srgrimes dd_close(); 1011556Srgrimes exit(0); 1021556Srgrimes} 1031556Srgrimes 1041556Srgrimesstatic void 1051556Srgrimessetup() 1061556Srgrimes{ 1071556Srgrimes u_int cnt; 10819720Sphk struct timeval tv; 1091556Srgrimes 1101556Srgrimes if (in.name == NULL) { 1111556Srgrimes in.name = "stdin"; 1121556Srgrimes in.fd = STDIN_FILENO; 1131556Srgrimes } else { 1141556Srgrimes in.fd = open(in.name, O_RDONLY, 0); 1151556Srgrimes if (in.fd < 0) 1161556Srgrimes err(1, "%s", in.name); 1171556Srgrimes } 1181556Srgrimes 1191556Srgrimes getfdtype(&in); 1201556Srgrimes 1211556Srgrimes if (files_cnt > 1 && !(in.flags & ISTAPE)) 1221556Srgrimes errx(1, "files is not supported for non-tape devices"); 1231556Srgrimes 1241556Srgrimes if (out.name == NULL) { 1251556Srgrimes /* No way to check for read access here. */ 1261556Srgrimes out.fd = STDOUT_FILENO; 1271556Srgrimes out.name = "stdout"; 1281556Srgrimes } else { 1291556Srgrimes#define OFLAGS \ 1301556Srgrimes (O_CREAT | (ddflags & (C_SEEK | C_NOTRUNC) ? 0 : O_TRUNC)) 1311556Srgrimes out.fd = open(out.name, O_RDWR | OFLAGS, DEFFILEMODE); 1321556Srgrimes /* 1331556Srgrimes * May not have read access, so try again with write only. 1341556Srgrimes * Without read we may have a problem if output also does 1351556Srgrimes * not support seeks. 1361556Srgrimes */ 1371556Srgrimes if (out.fd < 0) { 1381556Srgrimes out.fd = open(out.name, O_WRONLY | OFLAGS, DEFFILEMODE); 1391556Srgrimes out.flags |= NOREAD; 1401556Srgrimes } 1411556Srgrimes if (out.fd < 0) 1421556Srgrimes err(1, "%s", out.name); 1431556Srgrimes } 1441556Srgrimes 1451556Srgrimes getfdtype(&out); 1461556Srgrimes 1471556Srgrimes /* 1481556Srgrimes * Allocate space for the input and output buffers. If not doing 1491556Srgrimes * record oriented I/O, only need a single buffer. 1501556Srgrimes */ 1511556Srgrimes if (!(ddflags & (C_BLOCK|C_UNBLOCK))) { 1521556Srgrimes if ((in.db = malloc(out.dbsz + in.dbsz - 1)) == NULL) 1531556Srgrimes err(1, NULL); 1541556Srgrimes out.db = in.db; 1551556Srgrimes } else if ((in.db = 1561556Srgrimes malloc((u_int)(MAX(in.dbsz, cbsz) + cbsz))) == NULL || 1571556Srgrimes (out.db = malloc((u_int)(out.dbsz + cbsz))) == NULL) 1581556Srgrimes err(1, NULL); 1591556Srgrimes in.dbp = in.db; 1601556Srgrimes out.dbp = out.db; 1611556Srgrimes 1621556Srgrimes /* Position the input/output streams. */ 1631556Srgrimes if (in.offset) 1641556Srgrimes pos_in(); 1651556Srgrimes if (out.offset) 1661556Srgrimes pos_out(); 1671556Srgrimes 1681556Srgrimes /* 1691556Srgrimes * Truncate the output file; ignore errors because it fails on some 1701556Srgrimes * kinds of output files, tapes, for example. 1711556Srgrimes */ 1721556Srgrimes if (ddflags & (C_OF | C_SEEK | C_NOTRUNC) == (C_OF | C_SEEK)) 1731556Srgrimes (void)ftruncate(out.fd, (off_t)out.offset * out.dbsz); 1741556Srgrimes 1751556Srgrimes /* 1761556Srgrimes * If converting case at the same time as another conversion, build a 1771556Srgrimes * table that does both at once. If just converting case, use the 1781556Srgrimes * built-in tables. 1791556Srgrimes */ 1801556Srgrimes if (ddflags & (C_LCASE|C_UCASE)) 1811556Srgrimes if (ddflags & C_ASCII) 1821556Srgrimes if (ddflags & C_LCASE) { 1835702Sache for (cnt = 0; cnt <= 0377; ++cnt) 1841556Srgrimes if (isupper(ctab[cnt])) 1851556Srgrimes ctab[cnt] = tolower(ctab[cnt]); 1861556Srgrimes } else { 1875702Sache for (cnt = 0; cnt <= 0377; ++cnt) 1881556Srgrimes if (islower(ctab[cnt])) 1891556Srgrimes ctab[cnt] = toupper(ctab[cnt]); 1901556Srgrimes } 1911556Srgrimes else if (ddflags & C_EBCDIC) 1921556Srgrimes if (ddflags & C_LCASE) { 1935702Sache for (cnt = 0; cnt <= 0377; ++cnt) 1941556Srgrimes if (isupper(cnt)) 1951556Srgrimes ctab[cnt] = ctab[tolower(cnt)]; 1961556Srgrimes } else { 1975702Sache for (cnt = 0; cnt <= 0377; ++cnt) 1981556Srgrimes if (islower(cnt)) 1991556Srgrimes ctab[cnt] = ctab[toupper(cnt)]; 2001556Srgrimes } 2015701Sache else { 2021556Srgrimes ctab = ddflags & C_LCASE ? u2l : l2u; 2035701Sache if (ddflags & C_LCASE) { 2045702Sache for (cnt = 0; cnt <= 0377; ++cnt) 2055701Sache if (isupper(cnt)) 2065701Sache ctab[cnt] = tolower(cnt); 2075702Sache else 2085702Sache ctab[cnt] = cnt; 2095701Sache } else { 2105702Sache for (cnt = 0; cnt <= 0377; ++cnt) 2115701Sache if (islower(cnt)) 2125701Sache ctab[cnt] = toupper(cnt); 2135702Sache else 2145702Sache ctab[cnt] = cnt; 2155701Sache } 2165701Sache } 21719720Sphk (void)gettimeofday(&tv, (struct timezone *)NULL); 21819720Sphk st.start = tv.tv_sec + tv.tv_usec * 1e-6; 2191556Srgrimes} 2201556Srgrimes 2211556Srgrimesstatic void 2221556Srgrimesgetfdtype(io) 2231556Srgrimes IO *io; 2241556Srgrimes{ 2251556Srgrimes struct mtget mt; 2261556Srgrimes struct stat sb; 2271556Srgrimes 2281556Srgrimes if (fstat(io->fd, &sb)) 2291556Srgrimes err(1, "%s", io->name); 2301556Srgrimes if (S_ISCHR(sb.st_mode)) 2311556Srgrimes io->flags |= ioctl(io->fd, MTIOCGET, &mt) ? ISCHR : ISTAPE; 2321556Srgrimes else if (lseek(io->fd, (off_t)0, SEEK_CUR) == -1 && errno == ESPIPE) 2331556Srgrimes io->flags |= ISPIPE; /* XXX fixed in 4.4BSD */ 2341556Srgrimes} 2351556Srgrimes 2361556Srgrimesstatic void 2371556Srgrimesdd_in() 2381556Srgrimes{ 2391556Srgrimes int flags, n; 2401556Srgrimes 2411556Srgrimes for (flags = ddflags;;) { 2421556Srgrimes if (cpy_cnt && (st.in_full + st.in_part) >= cpy_cnt) 2431556Srgrimes return; 2441556Srgrimes 2451556Srgrimes /* 2461556Srgrimes * Zero the buffer first if trying to recover from errors so 2471556Srgrimes * lose the minimum amount of data. If doing block operations 2481556Srgrimes * use spaces. 2491556Srgrimes */ 2501556Srgrimes if ((flags & (C_NOERROR|C_SYNC)) == (C_NOERROR|C_SYNC)) 2511556Srgrimes if (flags & (C_BLOCK|C_UNBLOCK)) 2521556Srgrimes memset(in.dbp, ' ', in.dbsz); 2531556Srgrimes else 2541556Srgrimes memset(in.dbp, 0, in.dbsz); 2551556Srgrimes 2561556Srgrimes n = read(in.fd, in.dbp, in.dbsz); 2571556Srgrimes if (n == 0) { 2581556Srgrimes in.dbrcnt = 0; 2591556Srgrimes return; 2601556Srgrimes } 2611556Srgrimes 2621556Srgrimes /* Read error. */ 2631556Srgrimes if (n < 0) { 2641556Srgrimes /* 2651556Srgrimes * If noerror not specified, die. POSIX requires that 2661556Srgrimes * the warning message be followed by an I/O display. 2671556Srgrimes */ 2681556Srgrimes if (!(flags & C_NOERROR)) 2691556Srgrimes err(1, "%s", in.name); 2701556Srgrimes warn("%s", in.name); 2711556Srgrimes summary(); 2721556Srgrimes 2731556Srgrimes /* 2741556Srgrimes * If it's not a tape drive or a pipe, seek past the 2751556Srgrimes * error. If your OS doesn't do the right thing for 2761556Srgrimes * raw disks this section should be modified to re-read 2771556Srgrimes * in sector size chunks. 2781556Srgrimes */ 2791556Srgrimes if (!(in.flags & (ISPIPE|ISTAPE)) && 2801556Srgrimes lseek(in.fd, (off_t)in.dbsz, SEEK_CUR)) 2811556Srgrimes warn("%s", in.name); 2821556Srgrimes 2831556Srgrimes /* If sync not specified, omit block and continue. */ 2841556Srgrimes if (!(ddflags & C_SYNC)) 2851556Srgrimes continue; 2861556Srgrimes 2871556Srgrimes /* Read errors count as full blocks. */ 2881556Srgrimes in.dbcnt += in.dbrcnt = in.dbsz; 2891556Srgrimes ++st.in_full; 2901556Srgrimes 2911556Srgrimes /* Handle full input blocks. */ 2921556Srgrimes } else if (n == in.dbsz) { 2931556Srgrimes in.dbcnt += in.dbrcnt = n; 2941556Srgrimes ++st.in_full; 2951556Srgrimes 2961556Srgrimes /* Handle partial input blocks. */ 2971556Srgrimes } else { 2981556Srgrimes /* If sync, use the entire block. */ 2991556Srgrimes if (ddflags & C_SYNC) 3001556Srgrimes in.dbcnt += in.dbrcnt = in.dbsz; 3011556Srgrimes else 3021556Srgrimes in.dbcnt += in.dbrcnt = n; 3031556Srgrimes ++st.in_part; 3041556Srgrimes } 3051556Srgrimes 3061556Srgrimes /* 3071556Srgrimes * POSIX states that if bs is set and no other conversions 3081556Srgrimes * than noerror, notrunc or sync are specified, the block 3091556Srgrimes * is output without buffering as it is read. 3101556Srgrimes */ 3111556Srgrimes if (ddflags & C_BS) { 3121556Srgrimes out.dbcnt = in.dbcnt; 3131556Srgrimes dd_out(1); 3141556Srgrimes in.dbcnt = 0; 3151556Srgrimes continue; 3161556Srgrimes } 3171556Srgrimes 3181556Srgrimes if (ddflags & C_SWAB) { 3191556Srgrimes if ((n = in.dbcnt) & 1) { 3201556Srgrimes ++st.swab; 3211556Srgrimes --n; 3221556Srgrimes } 3231556Srgrimes swab(in.dbp, in.dbp, n); 3241556Srgrimes } 3251556Srgrimes 3261556Srgrimes in.dbp += in.dbrcnt; 3271556Srgrimes (*cfunc)(); 3281556Srgrimes } 3291556Srgrimes} 3301556Srgrimes 3311556Srgrimes/* 3321556Srgrimes * Cleanup any remaining I/O and flush output. If necesssary, output file 3331556Srgrimes * is truncated. 3341556Srgrimes */ 3351556Srgrimesstatic void 3361556Srgrimesdd_close() 3371556Srgrimes{ 3381556Srgrimes if (cfunc == def) 3391556Srgrimes def_close(); 3401556Srgrimes else if (cfunc == block) 3411556Srgrimes block_close(); 3421556Srgrimes else if (cfunc == unblock) 3431556Srgrimes unblock_close(); 3441556Srgrimes if (ddflags & C_OSYNC && out.dbcnt < out.dbsz) { 3451556Srgrimes memset(out.dbp, 0, out.dbsz - out.dbcnt); 3461556Srgrimes out.dbcnt = out.dbsz; 3471556Srgrimes } 3481556Srgrimes if (out.dbcnt) 3491556Srgrimes dd_out(1); 3501556Srgrimes} 3511556Srgrimes 3521556Srgrimesvoid 3531556Srgrimesdd_out(force) 3541556Srgrimes int force; 3551556Srgrimes{ 3561556Srgrimes static int warned; 3571556Srgrimes int cnt, n, nw; 3581556Srgrimes u_char *outp; 3591556Srgrimes 3601556Srgrimes /* 3611556Srgrimes * Write one or more blocks out. The common case is writing a full 3621556Srgrimes * output block in a single write; increment the full block stats. 3631556Srgrimes * Otherwise, we're into partial block writes. If a partial write, 3641556Srgrimes * and it's a character device, just warn. If a tape device, quit. 3651556Srgrimes * 3661556Srgrimes * The partial writes represent two cases. 1: Where the input block 3671556Srgrimes * was less than expected so the output block was less than expected. 3681556Srgrimes * 2: Where the input block was the right size but we were forced to 3691556Srgrimes * write the block in multiple chunks. The original versions of dd(1) 3701556Srgrimes * never wrote a block in more than a single write, so the latter case 3711556Srgrimes * never happened. 3721556Srgrimes * 3731556Srgrimes * One special case is if we're forced to do the write -- in that case 3741556Srgrimes * we play games with the buffer size, and it's usually a partial write. 3751556Srgrimes */ 3761556Srgrimes outp = out.db; 3771556Srgrimes for (n = force ? out.dbcnt : out.dbsz;; n = out.dbsz) { 3781556Srgrimes for (cnt = n;; cnt -= nw) { 3791556Srgrimes nw = write(out.fd, outp, cnt); 3801556Srgrimes if (nw <= 0) { 3811556Srgrimes if (nw == 0) 3821556Srgrimes errx(1, "%s: end of device", out.name); 3831556Srgrimes if (errno != EINTR) 3841556Srgrimes err(1, "%s", out.name); 3851556Srgrimes nw = 0; 3861556Srgrimes } 3871556Srgrimes outp += nw; 3881556Srgrimes st.bytes += nw; 3891556Srgrimes if (nw == n) { 3901556Srgrimes if (n != out.dbsz) 3911556Srgrimes ++st.out_part; 3921556Srgrimes else 3931556Srgrimes ++st.out_full; 3941556Srgrimes break; 3951556Srgrimes } 3961556Srgrimes ++st.out_part; 3971556Srgrimes if (nw == cnt) 3981556Srgrimes break; 3991556Srgrimes if (out.flags & ISCHR && !warned) { 4001556Srgrimes warned = 1; 4011556Srgrimes warnx("%s: short write on character device", 4021556Srgrimes out.name); 4031556Srgrimes } 4041556Srgrimes if (out.flags & ISTAPE) 4051556Srgrimes errx(1, "%s: short write on tape device", out.name); 4061556Srgrimes } 4071556Srgrimes if ((out.dbcnt -= n) < out.dbsz) 4081556Srgrimes break; 4091556Srgrimes } 4101556Srgrimes 4111556Srgrimes /* Reassemble the output block. */ 4121556Srgrimes if (out.dbcnt) 4131556Srgrimes memmove(out.db, out.dbp - out.dbcnt, out.dbcnt); 4141556Srgrimes out.dbp = out.db + out.dbcnt; 4151556Srgrimes} 416