dd.c revision 28430
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 *
3728430Sjlemon *	$Id: dd.c,v 1.10 1997/02/22 14:02:44 peter Exp $
381556Srgrimes */
391556Srgrimes
401556Srgrimes#ifndef lint
4120420Sstevestatic char const 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
4720420Sstevestatic char const 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	 */
17220420Ssteve	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{
23928430Sjlemon	int n;
2401556Srgrimes
24128430Sjlemon	for (;;) {
2421556Srgrimes		if (cpy_cnt && (st.in_full + st.in_part) >= cpy_cnt)
2431556Srgrimes			return;
2441556Srgrimes
2451556Srgrimes		/*
24628430Sjlemon		 * Zero the buffer first if sync; If doing block operations
2471556Srgrimes		 * use spaces.
2481556Srgrimes		 */
24928430Sjlemon		if (ddflags & C_SYNC)
25028430Sjlemon			if (ddflags & (C_BLOCK|C_UNBLOCK))
2511556Srgrimes				memset(in.dbp, ' ', in.dbsz);
2521556Srgrimes			else
2531556Srgrimes				memset(in.dbp, 0, in.dbsz);
2541556Srgrimes
2551556Srgrimes		n = read(in.fd, in.dbp, in.dbsz);
2561556Srgrimes		if (n == 0) {
2571556Srgrimes			in.dbrcnt = 0;
2581556Srgrimes			return;
2591556Srgrimes		}
2601556Srgrimes
2611556Srgrimes		/* Read error. */
2621556Srgrimes		if (n < 0) {
2631556Srgrimes			/*
2641556Srgrimes			 * If noerror not specified, die.  POSIX requires that
2651556Srgrimes			 * the warning message be followed by an I/O display.
2661556Srgrimes			 */
26728430Sjlemon			if (!(ddflags & C_NOERROR))
2681556Srgrimes				err(1, "%s", in.name);
2691556Srgrimes			warn("%s", in.name);
2701556Srgrimes			summary();
2711556Srgrimes
2721556Srgrimes			/*
2731556Srgrimes			 * If it's not a tape drive or a pipe, seek past the
2741556Srgrimes			 * error.  If your OS doesn't do the right thing for
2751556Srgrimes			 * raw disks this section should be modified to re-read
2761556Srgrimes			 * in sector size chunks.
2771556Srgrimes			 */
2781556Srgrimes			if (!(in.flags & (ISPIPE|ISTAPE)) &&
2791556Srgrimes			    lseek(in.fd, (off_t)in.dbsz, SEEK_CUR))
2801556Srgrimes				warn("%s", in.name);
2811556Srgrimes
2821556Srgrimes			/* If sync not specified, omit block and continue. */
2831556Srgrimes			if (!(ddflags & C_SYNC))
2841556Srgrimes				continue;
2851556Srgrimes
2861556Srgrimes			/* Read errors count as full blocks. */
2871556Srgrimes			in.dbcnt += in.dbrcnt = in.dbsz;
2881556Srgrimes			++st.in_full;
2891556Srgrimes
2901556Srgrimes		/* Handle full input blocks. */
2911556Srgrimes		} else if (n == in.dbsz) {
2921556Srgrimes			in.dbcnt += in.dbrcnt = n;
2931556Srgrimes			++st.in_full;
2941556Srgrimes
2951556Srgrimes		/* Handle partial input blocks. */
2961556Srgrimes		} else {
2971556Srgrimes			/* If sync, use the entire block. */
2981556Srgrimes			if (ddflags & C_SYNC)
2991556Srgrimes				in.dbcnt += in.dbrcnt = in.dbsz;
3001556Srgrimes			else
3011556Srgrimes				in.dbcnt += in.dbrcnt = n;
3021556Srgrimes			++st.in_part;
3031556Srgrimes		}
3041556Srgrimes
3051556Srgrimes		/*
3061556Srgrimes		 * POSIX states that if bs is set and no other conversions
3071556Srgrimes		 * than noerror, notrunc or sync are specified, the block
3081556Srgrimes		 * is output without buffering as it is read.
3091556Srgrimes		 */
3101556Srgrimes		if (ddflags & C_BS) {
3111556Srgrimes			out.dbcnt = in.dbcnt;
3121556Srgrimes			dd_out(1);
3131556Srgrimes			in.dbcnt = 0;
3141556Srgrimes			continue;
3151556Srgrimes		}
3161556Srgrimes
3171556Srgrimes		if (ddflags & C_SWAB) {
3181556Srgrimes			if ((n = in.dbcnt) & 1) {
3191556Srgrimes				++st.swab;
3201556Srgrimes				--n;
3211556Srgrimes			}
3221556Srgrimes			swab(in.dbp, in.dbp, n);
3231556Srgrimes		}
3241556Srgrimes
3251556Srgrimes		in.dbp += in.dbrcnt;
3261556Srgrimes		(*cfunc)();
3271556Srgrimes	}
3281556Srgrimes}
3291556Srgrimes
3301556Srgrimes/*
3311556Srgrimes * Cleanup any remaining I/O and flush output.  If necesssary, output file
3321556Srgrimes * is truncated.
3331556Srgrimes */
3341556Srgrimesstatic void
3351556Srgrimesdd_close()
3361556Srgrimes{
3371556Srgrimes	if (cfunc == def)
3381556Srgrimes		def_close();
3391556Srgrimes	else if (cfunc == block)
3401556Srgrimes		block_close();
3411556Srgrimes	else if (cfunc == unblock)
3421556Srgrimes		unblock_close();
34328430Sjlemon	if (ddflags & C_OSYNC && out.dbcnt && out.dbcnt < out.dbsz) {
34428430Sjlemon		if (ddflags & (C_BLOCK|C_UNBLOCK))
34528430Sjlemon			memset(out.dbp, ' ', out.dbsz - out.dbcnt);
34628430Sjlemon		else
34728430Sjlemon			memset(out.dbp, 0, out.dbsz - out.dbcnt);
3481556Srgrimes		out.dbcnt = out.dbsz;
3491556Srgrimes	}
3501556Srgrimes	if (out.dbcnt)
3511556Srgrimes		dd_out(1);
3521556Srgrimes}
3531556Srgrimes
3541556Srgrimesvoid
3551556Srgrimesdd_out(force)
3561556Srgrimes	int force;
3571556Srgrimes{
3581556Srgrimes	static int warned;
3591556Srgrimes	int cnt, n, nw;
3601556Srgrimes	u_char *outp;
3611556Srgrimes
3621556Srgrimes	/*
3631556Srgrimes	 * Write one or more blocks out.  The common case is writing a full
3641556Srgrimes	 * output block in a single write; increment the full block stats.
3651556Srgrimes	 * Otherwise, we're into partial block writes.  If a partial write,
3661556Srgrimes	 * and it's a character device, just warn.  If a tape device, quit.
3671556Srgrimes	 *
3681556Srgrimes	 * The partial writes represent two cases.  1: Where the input block
3691556Srgrimes	 * was less than expected so the output block was less than expected.
3701556Srgrimes	 * 2: Where the input block was the right size but we were forced to
3711556Srgrimes	 * write the block in multiple chunks.  The original versions of dd(1)
3721556Srgrimes	 * never wrote a block in more than a single write, so the latter case
3731556Srgrimes	 * never happened.
3741556Srgrimes	 *
3751556Srgrimes	 * One special case is if we're forced to do the write -- in that case
3761556Srgrimes	 * we play games with the buffer size, and it's usually a partial write.
3771556Srgrimes	 */
3781556Srgrimes	outp = out.db;
3791556Srgrimes	for (n = force ? out.dbcnt : out.dbsz;; n = out.dbsz) {
3801556Srgrimes		for (cnt = n;; cnt -= nw) {
3811556Srgrimes			nw = write(out.fd, outp, cnt);
3821556Srgrimes			if (nw <= 0) {
3831556Srgrimes				if (nw == 0)
3841556Srgrimes					errx(1, "%s: end of device", out.name);
3851556Srgrimes				if (errno != EINTR)
3861556Srgrimes					err(1, "%s", out.name);
3871556Srgrimes				nw = 0;
3881556Srgrimes			}
3891556Srgrimes			outp += nw;
3901556Srgrimes			st.bytes += nw;
3911556Srgrimes			if (nw == n) {
3921556Srgrimes				if (n != out.dbsz)
3931556Srgrimes					++st.out_part;
3941556Srgrimes				else
3951556Srgrimes					++st.out_full;
3961556Srgrimes				break;
3971556Srgrimes			}
3981556Srgrimes			++st.out_part;
3991556Srgrimes			if (nw == cnt)
4001556Srgrimes				break;
4011556Srgrimes			if (out.flags & ISCHR && !warned) {
4021556Srgrimes				warned = 1;
4031556Srgrimes				warnx("%s: short write on character device",
4041556Srgrimes				    out.name);
4051556Srgrimes			}
4061556Srgrimes			if (out.flags & ISTAPE)
4071556Srgrimes				errx(1, "%s: short write on tape device", out.name);
4081556Srgrimes		}
4091556Srgrimes		if ((out.dbcnt -= n) < out.dbsz)
4101556Srgrimes			break;
4111556Srgrimes	}
4121556Srgrimes
4131556Srgrimes	/* Reassemble the output block. */
4141556Srgrimes	if (out.dbcnt)
4151556Srgrimes		memmove(out.db, out.dbp - out.dbcnt, out.dbcnt);
4161556Srgrimes	out.dbp = out.db + out.dbcnt;
4171556Srgrimes}
418