dd.c revision 46073
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.
361556Srgrimes */
371556Srgrimes
381556Srgrimes#ifndef lint
3920420Sstevestatic char const copyright[] =
401556Srgrimes"@(#) Copyright (c) 1991, 1993, 1994\n\
411556Srgrimes	The Regents of the University of California.  All rights reserved.\n";
421556Srgrimes#endif /* not lint */
431556Srgrimes
441556Srgrimes#ifndef lint
4535773Scharnier#if 0
4636007Scharnierstatic char sccsid[] = "@(#)dd.c	8.5 (Berkeley) 4/2/94";
4735773Scharnier#endif
4835773Scharnierstatic const char rcsid[] =
4946073Simp	"$Id: dd.c,v 1.15 1998/05/13 07:33:47 charnier Exp $";
501556Srgrimes#endif /* not lint */
511556Srgrimes
521556Srgrimes#include <sys/param.h>
531556Srgrimes#include <sys/stat.h>
541556Srgrimes#include <sys/mtio.h>
551556Srgrimes
561556Srgrimes#include <ctype.h>
571556Srgrimes#include <err.h>
581556Srgrimes#include <errno.h>
591556Srgrimes#include <fcntl.h>
6019720Sphk#include <locale.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 */
7830312Sjoergu_long	pending = 0;		/* pending seek if sparse */
791556Srgrimesu_int	ddflags;		/* conversion options */
801556Srgrimesu_int	cbsz;			/* conversion block size */
811556Srgrimesu_int	files_cnt = 1;		/* # of files to copy */
821556Srgrimesu_char	*ctab;			/* conversion table */
831556Srgrimes
841556Srgrimesint
851556Srgrimesmain(argc, argv)
861556Srgrimes	int argc;
871556Srgrimes	char *argv[];
881556Srgrimes{
8919720Sphk	(void)setlocale(LC_CTYPE, "");
901556Srgrimes	jcl(argv);
911556Srgrimes	setup();
921556Srgrimes
931556Srgrimes	(void)signal(SIGINFO, summaryx);
941556Srgrimes	(void)signal(SIGINT, terminate);
951556Srgrimes
961556Srgrimes	atexit(summary);
971556Srgrimes
981556Srgrimes	while (files_cnt--)
991556Srgrimes		dd_in();
1001556Srgrimes
1011556Srgrimes	dd_close();
1021556Srgrimes	exit(0);
1031556Srgrimes}
1041556Srgrimes
1051556Srgrimesstatic void
1061556Srgrimessetup()
1071556Srgrimes{
1081556Srgrimes	u_int cnt;
10919720Sphk	struct timeval tv;
1101556Srgrimes
1111556Srgrimes	if (in.name == NULL) {
1121556Srgrimes		in.name = "stdin";
1131556Srgrimes		in.fd = STDIN_FILENO;
1141556Srgrimes	} else {
1151556Srgrimes		in.fd = open(in.name, O_RDONLY, 0);
1161556Srgrimes		if (in.fd < 0)
1171556Srgrimes			err(1, "%s", in.name);
1181556Srgrimes	}
1191556Srgrimes
1201556Srgrimes	getfdtype(&in);
1211556Srgrimes
1221556Srgrimes	if (files_cnt > 1 && !(in.flags & ISTAPE))
1231556Srgrimes		errx(1, "files is not supported for non-tape devices");
1241556Srgrimes
1251556Srgrimes	if (out.name == NULL) {
1261556Srgrimes		/* No way to check for read access here. */
1271556Srgrimes		out.fd = STDOUT_FILENO;
1281556Srgrimes		out.name = "stdout";
1291556Srgrimes	} else {
1301556Srgrimes#define	OFLAGS \
1311556Srgrimes    (O_CREAT | (ddflags & (C_SEEK | C_NOTRUNC) ? 0 : O_TRUNC))
1321556Srgrimes		out.fd = open(out.name, O_RDWR | OFLAGS, DEFFILEMODE);
1331556Srgrimes		/*
1341556Srgrimes		 * May not have read access, so try again with write only.
1351556Srgrimes		 * Without read we may have a problem if output also does
1361556Srgrimes		 * not support seeks.
1371556Srgrimes		 */
1381556Srgrimes		if (out.fd < 0) {
1391556Srgrimes			out.fd = open(out.name, O_WRONLY | OFLAGS, DEFFILEMODE);
1401556Srgrimes			out.flags |= NOREAD;
1411556Srgrimes		}
1421556Srgrimes		if (out.fd < 0)
1431556Srgrimes			err(1, "%s", out.name);
1441556Srgrimes	}
1451556Srgrimes
1461556Srgrimes	getfdtype(&out);
1471556Srgrimes
1481556Srgrimes	/*
1491556Srgrimes	 * Allocate space for the input and output buffers.  If not doing
1501556Srgrimes	 * record oriented I/O, only need a single buffer.
1511556Srgrimes	 */
1521556Srgrimes	if (!(ddflags & (C_BLOCK|C_UNBLOCK))) {
1531556Srgrimes		if ((in.db = malloc(out.dbsz + in.dbsz - 1)) == NULL)
1541556Srgrimes			err(1, NULL);
1551556Srgrimes		out.db = in.db;
1561556Srgrimes	} else if ((in.db =
1571556Srgrimes	    malloc((u_int)(MAX(in.dbsz, cbsz) + cbsz))) == NULL ||
1581556Srgrimes	    (out.db = malloc((u_int)(out.dbsz + cbsz))) == NULL)
1591556Srgrimes		err(1, NULL);
1601556Srgrimes	in.dbp = in.db;
1611556Srgrimes	out.dbp = out.db;
1621556Srgrimes
1631556Srgrimes	/* Position the input/output streams. */
1641556Srgrimes	if (in.offset)
1651556Srgrimes		pos_in();
1661556Srgrimes	if (out.offset)
1671556Srgrimes		pos_out();
1681556Srgrimes
1691556Srgrimes	/*
1701556Srgrimes	 * Truncate the output file; ignore errors because it fails on some
1711556Srgrimes	 * kinds of output files, tapes, for example.
1721556Srgrimes	 */
17320420Ssteve	if ((ddflags & (C_OF | C_SEEK | C_NOTRUNC)) == (C_OF | C_SEEK))
1741556Srgrimes		(void)ftruncate(out.fd, (off_t)out.offset * out.dbsz);
1751556Srgrimes
1761556Srgrimes	/*
1771556Srgrimes	 * If converting case at the same time as another conversion, build a
1781556Srgrimes	 * table that does both at once.  If just converting case, use the
1791556Srgrimes	 * built-in tables.
1801556Srgrimes	 */
18146073Simp	if (ddflags & (C_LCASE|C_UCASE)) {
18246073Simp		if (ddflags & C_ASCII) {
1831556Srgrimes			if (ddflags & C_LCASE) {
1845702Sache				for (cnt = 0; cnt <= 0377; ++cnt)
1851556Srgrimes					if (isupper(ctab[cnt]))
1861556Srgrimes						ctab[cnt] = tolower(ctab[cnt]);
1871556Srgrimes			} else {
1885702Sache				for (cnt = 0; cnt <= 0377; ++cnt)
1891556Srgrimes					if (islower(ctab[cnt]))
1901556Srgrimes						ctab[cnt] = toupper(ctab[cnt]);
1911556Srgrimes			}
19246073Simp		} else if (ddflags & C_EBCDIC) {
1931556Srgrimes			if (ddflags & C_LCASE) {
1945702Sache				for (cnt = 0; cnt <= 0377; ++cnt)
1951556Srgrimes					if (isupper(cnt))
1961556Srgrimes						ctab[cnt] = ctab[tolower(cnt)];
1971556Srgrimes			} else {
1985702Sache				for (cnt = 0; cnt <= 0377; ++cnt)
1991556Srgrimes					if (islower(cnt))
2001556Srgrimes						ctab[cnt] = ctab[toupper(cnt)];
2011556Srgrimes			}
20246073Simp		} else {
2031556Srgrimes			ctab = ddflags & C_LCASE ? u2l : l2u;
2045701Sache			if (ddflags & C_LCASE) {
2055702Sache				for (cnt = 0; cnt <= 0377; ++cnt)
2065701Sache					if (isupper(cnt))
2075701Sache						ctab[cnt] = tolower(cnt);
2085702Sache					else
2095702Sache						ctab[cnt] = cnt;
2105701Sache			} else {
2115702Sache				for (cnt = 0; cnt <= 0377; ++cnt)
2125701Sache					if (islower(cnt))
2135701Sache						ctab[cnt] = toupper(cnt);
2145702Sache					else
2155702Sache						ctab[cnt] = cnt;
2165701Sache			}
2175701Sache		}
21846073Simp	}
21919720Sphk	(void)gettimeofday(&tv, (struct timezone *)NULL);
22019720Sphk	st.start = tv.tv_sec + tv.tv_usec * 1e-6;
2211556Srgrimes}
2221556Srgrimes
2231556Srgrimesstatic void
2241556Srgrimesgetfdtype(io)
2251556Srgrimes	IO *io;
2261556Srgrimes{
2271556Srgrimes	struct mtget mt;
2281556Srgrimes	struct stat sb;
2291556Srgrimes
2301556Srgrimes	if (fstat(io->fd, &sb))
2311556Srgrimes		err(1, "%s", io->name);
2321556Srgrimes	if (S_ISCHR(sb.st_mode))
2331556Srgrimes		io->flags |= ioctl(io->fd, MTIOCGET, &mt) ? ISCHR : ISTAPE;
2341556Srgrimes	else if (lseek(io->fd, (off_t)0, SEEK_CUR) == -1 && errno == ESPIPE)
2351556Srgrimes		io->flags |= ISPIPE;		/* XXX fixed in 4.4BSD */
2361556Srgrimes}
2371556Srgrimes
2381556Srgrimesstatic void
2391556Srgrimesdd_in()
2401556Srgrimes{
24128430Sjlemon	int n;
2421556Srgrimes
24328430Sjlemon	for (;;) {
2441556Srgrimes		if (cpy_cnt && (st.in_full + st.in_part) >= cpy_cnt)
2451556Srgrimes			return;
2461556Srgrimes
2471556Srgrimes		/*
24828430Sjlemon		 * Zero the buffer first if sync; If doing block operations
2491556Srgrimes		 * use spaces.
2501556Srgrimes		 */
25128430Sjlemon		if (ddflags & C_SYNC)
25228430Sjlemon			if (ddflags & (C_BLOCK|C_UNBLOCK))
2531556Srgrimes				memset(in.dbp, ' ', in.dbsz);
2541556Srgrimes			else
2551556Srgrimes				memset(in.dbp, 0, in.dbsz);
2561556Srgrimes
2571556Srgrimes		n = read(in.fd, in.dbp, in.dbsz);
2581556Srgrimes		if (n == 0) {
2591556Srgrimes			in.dbrcnt = 0;
2601556Srgrimes			return;
2611556Srgrimes		}
2621556Srgrimes
2631556Srgrimes		/* Read error. */
2641556Srgrimes		if (n < 0) {
2651556Srgrimes			/*
2661556Srgrimes			 * If noerror not specified, die.  POSIX requires that
2671556Srgrimes			 * the warning message be followed by an I/O display.
2681556Srgrimes			 */
26928430Sjlemon			if (!(ddflags & C_NOERROR))
2701556Srgrimes				err(1, "%s", in.name);
2711556Srgrimes			warn("%s", in.name);
2721556Srgrimes			summary();
2731556Srgrimes
2741556Srgrimes			/*
2751556Srgrimes			 * If it's not a tape drive or a pipe, seek past the
2761556Srgrimes			 * error.  If your OS doesn't do the right thing for
2771556Srgrimes			 * raw disks this section should be modified to re-read
2781556Srgrimes			 * in sector size chunks.
2791556Srgrimes			 */
2801556Srgrimes			if (!(in.flags & (ISPIPE|ISTAPE)) &&
2811556Srgrimes			    lseek(in.fd, (off_t)in.dbsz, SEEK_CUR))
2821556Srgrimes				warn("%s", in.name);
2831556Srgrimes
2841556Srgrimes			/* If sync not specified, omit block and continue. */
2851556Srgrimes			if (!(ddflags & C_SYNC))
2861556Srgrimes				continue;
2871556Srgrimes
2881556Srgrimes			/* Read errors count as full blocks. */
2891556Srgrimes			in.dbcnt += in.dbrcnt = in.dbsz;
2901556Srgrimes			++st.in_full;
2911556Srgrimes
2921556Srgrimes		/* Handle full input blocks. */
2931556Srgrimes		} else if (n == in.dbsz) {
2941556Srgrimes			in.dbcnt += in.dbrcnt = n;
2951556Srgrimes			++st.in_full;
2961556Srgrimes
2971556Srgrimes		/* Handle partial input blocks. */
2981556Srgrimes		} else {
2991556Srgrimes			/* If sync, use the entire block. */
3001556Srgrimes			if (ddflags & C_SYNC)
3011556Srgrimes				in.dbcnt += in.dbrcnt = in.dbsz;
3021556Srgrimes			else
3031556Srgrimes				in.dbcnt += in.dbrcnt = n;
3041556Srgrimes			++st.in_part;
3051556Srgrimes		}
3061556Srgrimes
3071556Srgrimes		/*
3081556Srgrimes		 * POSIX states that if bs is set and no other conversions
3091556Srgrimes		 * than noerror, notrunc or sync are specified, the block
3101556Srgrimes		 * is output without buffering as it is read.
3111556Srgrimes		 */
3121556Srgrimes		if (ddflags & C_BS) {
3131556Srgrimes			out.dbcnt = in.dbcnt;
3141556Srgrimes			dd_out(1);
3151556Srgrimes			in.dbcnt = 0;
3161556Srgrimes			continue;
3171556Srgrimes		}
3181556Srgrimes
3191556Srgrimes		if (ddflags & C_SWAB) {
32032324Sjoerg			if ((n = in.dbrcnt) & 1) {
3211556Srgrimes				++st.swab;
3221556Srgrimes				--n;
3231556Srgrimes			}
3241556Srgrimes			swab(in.dbp, in.dbp, n);
3251556Srgrimes		}
3261556Srgrimes
3271556Srgrimes		in.dbp += in.dbrcnt;
3281556Srgrimes		(*cfunc)();
3291556Srgrimes	}
3301556Srgrimes}
3311556Srgrimes
3321556Srgrimes/*
3331556Srgrimes * Cleanup any remaining I/O and flush output.  If necesssary, output file
3341556Srgrimes * is truncated.
3351556Srgrimes */
3361556Srgrimesstatic void
3371556Srgrimesdd_close()
3381556Srgrimes{
3391556Srgrimes	if (cfunc == def)
3401556Srgrimes		def_close();
3411556Srgrimes	else if (cfunc == block)
3421556Srgrimes		block_close();
3431556Srgrimes	else if (cfunc == unblock)
3441556Srgrimes		unblock_close();
34528430Sjlemon	if (ddflags & C_OSYNC && out.dbcnt && out.dbcnt < out.dbsz) {
34628430Sjlemon		if (ddflags & (C_BLOCK|C_UNBLOCK))
34728430Sjlemon			memset(out.dbp, ' ', out.dbsz - out.dbcnt);
34828430Sjlemon		else
34928430Sjlemon			memset(out.dbp, 0, out.dbsz - out.dbcnt);
3501556Srgrimes		out.dbcnt = out.dbsz;
3511556Srgrimes	}
35230312Sjoerg	if (out.dbcnt || pending)
3531556Srgrimes		dd_out(1);
3541556Srgrimes}
3551556Srgrimes
3561556Srgrimesvoid
3571556Srgrimesdd_out(force)
3581556Srgrimes	int force;
3591556Srgrimes{
3601556Srgrimes	static int warned;
36130312Sjoerg	int cnt, n, nw, i, sparse;
3621556Srgrimes	u_char *outp;
3631556Srgrimes
3641556Srgrimes	/*
3651556Srgrimes	 * Write one or more blocks out.  The common case is writing a full
3661556Srgrimes	 * output block in a single write; increment the full block stats.
3671556Srgrimes	 * Otherwise, we're into partial block writes.  If a partial write,
3681556Srgrimes	 * and it's a character device, just warn.  If a tape device, quit.
3691556Srgrimes	 *
3701556Srgrimes	 * The partial writes represent two cases.  1: Where the input block
3711556Srgrimes	 * was less than expected so the output block was less than expected.
3721556Srgrimes	 * 2: Where the input block was the right size but we were forced to
3731556Srgrimes	 * write the block in multiple chunks.  The original versions of dd(1)
3741556Srgrimes	 * never wrote a block in more than a single write, so the latter case
3751556Srgrimes	 * never happened.
3761556Srgrimes	 *
3771556Srgrimes	 * One special case is if we're forced to do the write -- in that case
3781556Srgrimes	 * we play games with the buffer size, and it's usually a partial write.
3791556Srgrimes	 */
3801556Srgrimes	outp = out.db;
3811556Srgrimes	for (n = force ? out.dbcnt : out.dbsz;; n = out.dbsz) {
3821556Srgrimes		for (cnt = n;; cnt -= nw) {
38330312Sjoerg			sparse = 0;
38430312Sjoerg			if (ddflags & C_SPARSE) {
38530312Sjoerg				sparse = 1;	/* Is buffer sparse? */
38630312Sjoerg				for (i = 0; i < cnt; i++)
38730312Sjoerg					if (outp[i] != 0) {
38830312Sjoerg						sparse = 0;
38930312Sjoerg						break;
39030312Sjoerg					}
39130312Sjoerg			}
39230312Sjoerg			if (sparse && !force) {
39330312Sjoerg				pending += cnt;
39430312Sjoerg				nw = cnt;
39530312Sjoerg			} else {
39630312Sjoerg				if (pending != 0) {
39730312Sjoerg					if (force)
39830312Sjoerg						pending--;
39930312Sjoerg					if (lseek (out.fd, pending, SEEK_CUR) == -1)
40030312Sjoerg						err(2, "%s: seek error creating sparse file",
40130312Sjoerg						    out.name);
40230312Sjoerg					if (force)
40330312Sjoerg						write(out.fd, outp, 1);
40430312Sjoerg					pending = 0;
40530312Sjoerg				}
40630312Sjoerg				if (cnt)
40730312Sjoerg					nw = write(out.fd, outp, cnt);
40830312Sjoerg				else
40930312Sjoerg					return;
41030312Sjoerg			}
41130312Sjoerg
4121556Srgrimes			if (nw <= 0) {
4131556Srgrimes				if (nw == 0)
4141556Srgrimes					errx(1, "%s: end of device", out.name);
4151556Srgrimes				if (errno != EINTR)
4161556Srgrimes					err(1, "%s", out.name);
4171556Srgrimes				nw = 0;
4181556Srgrimes			}
4191556Srgrimes			outp += nw;
4201556Srgrimes			st.bytes += nw;
4211556Srgrimes			if (nw == n) {
4221556Srgrimes				if (n != out.dbsz)
4231556Srgrimes					++st.out_part;
4241556Srgrimes				else
4251556Srgrimes					++st.out_full;
4261556Srgrimes				break;
4271556Srgrimes			}
4281556Srgrimes			++st.out_part;
4291556Srgrimes			if (nw == cnt)
4301556Srgrimes				break;
4311556Srgrimes			if (out.flags & ISCHR && !warned) {
4321556Srgrimes				warned = 1;
4331556Srgrimes				warnx("%s: short write on character device",
4341556Srgrimes				    out.name);
4351556Srgrimes			}
4361556Srgrimes			if (out.flags & ISTAPE)
4371556Srgrimes				errx(1, "%s: short write on tape device", out.name);
4381556Srgrimes		}
4391556Srgrimes		if ((out.dbcnt -= n) < out.dbsz)
4401556Srgrimes			break;
4411556Srgrimes	}
4421556Srgrimes
4431556Srgrimes	/* Reassemble the output block. */
4441556Srgrimes	if (out.dbcnt)
4451556Srgrimes		memmove(out.db, out.dbp - out.dbcnt, out.dbcnt);
4461556Srgrimes	out.dbp = out.db + out.dbcnt;
4471556Srgrimes}
448