* Copyright (c) 1991 The Regents of the University of California.
* This code is derived from software contributed to Berkeley by
* Keith Muller of the University of California, San Diego and Lance
* Visser of Convex Computer Corporation.
* %sccs.include.redist.c%
"@(#) Copyright (c) 1991 The Regents of the University of California.\n\
static char sccsid
[] = "@(#)dd.c 5.8 (Berkeley) %G%";
static void dd_close
__P((void));
static void dd_in
__P((void));
static void setup
__P((void));
IO in
, out
; /* input/output state */
STAT st
; /* statistics */
void (*cfunc
)(); /* conversion function */
u_long cpy_cnt
; /* # of blocks to copy */
u_int ddflags
; /* conversion options */
u_int cbsz
; /* conversion block size */
u_int files_cnt
= 1; /* # of files to copy */
u_char
*ctab
; /* conversion table */
(void)signal(SIGINFO
, summary
);
(void)signal(SIGINT
, terminate
);
in
.fd
= open(in
.name
, O_RDONLY
, 0);
err("%s: %s", in
.name
, strerror(errno
));
err("%s: %s", in
.name
, strerror(errno
));
in
.flags
|= ioctl(in
.fd
, MTIOCGET
, &mt
) ? ISCHR
: ISTAPE
;
else if (lseek(in
.fd
, 0L, SEEK_CUR
) == -1 && errno
== ESPIPE
)
in
.flags
|= ISPIPE
; /* XXX fixed in 4.4BSD */
if (files_cnt
> 1 && !(in
.flags
& ISTAPE
))
err("files is not supported for non-tape devices");
/* No way to check for read access here. */
out
.fd
= open(out
.name
, O_RDWR
|O_CREAT
, DEFFILEMODE
);
* May not have read access, so try again with write only.
* Without read we may have a problem if output also does
out
.fd
= open(out
.name
, O_WRONLY
|O_CREAT
, DEFFILEMODE
);
err("%s: %s", out
.name
, strerror(errno
));
err("%s: %s", out
.name
, strerror(errno
));
out
.flags
|= ioctl(out
.fd
, MTIOCGET
, &mt
) ? ISCHR
: ISTAPE
;
else if (lseek(out
.fd
, 0L, SEEK_CUR
) == -1 && errno
== ESPIPE
)
out
.flags
|= ISPIPE
; /* XXX fixed in 4.4BSD */
* Allocate space for the input and output buffers. If not doing
* record oriented I/O, only need a single buffer.
if (!(ddflags
& (C_BLOCK
|C_UNBLOCK
))) {
cnt
= in
.dbsz
- 1 - in
.dbsz
/ 2 + in
.dbsz
;
else if (in
.dbsz
< out
.dbsz
)
cnt
= out
.dbsz
+ in
.dbsz
- 1;
if ((in
.db
= malloc(cnt
)) == NULL
)
err("%s", strerror(errno
));
malloc((u_int
)(MAX(in
.dbsz
, cbsz
) + cbsz
))) == NULL
||
(out
.db
= malloc((u_int
)(out
.dbsz
+ cbsz
))) == NULL
)
err("%s", strerror(errno
));
/* Position the input/output streams. */
/* Truncate the output file. */
if (ddflags
& C_NOTRUNC
) {
err("notrunc is not supported for tape devices");
} else if (S_ISREG(sb
.st_mode
) && ftruncate(out
.fd
,
(off_t
)out
.offset
* out
.dbsz
))
err("%s: truncate: %s", out
.name
, strerror(errno
));
* If converting case at the same time as another conversion, build a
* table that does both at once. If just converting case, use the
if (ddflags
& (C_LCASE
|C_UCASE
))
for (cnt
= 0; cnt
< 0377; ++cnt
)
ctab
[cnt
] = tolower(ctab
[cnt
]);
for (cnt
= 0; cnt
< 0377; ++cnt
)
ctab
[cnt
] = toupper(ctab
[cnt
]);
else if (ddflags
& C_EBCDIC
)
for (cnt
= 0; cnt
< 0377; ++cnt
)
ctab
[cnt
] = ctab
[tolower(cnt
)];
for (cnt
= 0; cnt
< 0377; ++cnt
)
ctab
[cnt
] = ctab
[toupper(cnt
)];
ctab
= ddflags
& C_LCASE
? u2l
: l2u
;
if (cpy_cnt
&& (st
.in_full
+ st
.in_part
) >= cpy_cnt
)
* Zero the buffer first if trying to recover from errors so
* lose the minimum amount of data. If doing block operations
if (flags
& (C_NOERROR
|C_SYNC
))
if (flags
& (C_BLOCK
|C_UNBLOCK
))
memset(in
.dbp
, ' ', in
.dbsz
);
n
= read(in
.fd
, in
.dbp
, in
.dbsz
);
* If noerror not specified, die. POSIX requires that
* the warning message be followed by an I/O display.
if (!(flags
& C_NOERROR
))
err("%s: %s", in
.name
, strerror(errno
));
warn("%s: %s", in
.name
, strerror(errno
));
* If it's not a tape drive or a pipe, seek past the
* error. If your OS doesn't do the right thing for
* raw disks this section should be modified to re-read
if (!(in
.flags
& (ISPIPE
|ISTAPE
)) &&
lseek(in
.fd
, (off_t
)in
.dbsz
, SEEK_CUR
))
warn("%s: %s", in
.name
, strerror(errno
));
/* If sync not specified, omit block and continue. */
/* Read errors count as full blocks. */
in
.dbcnt
+= in
.dbrcnt
= in
.dbsz
;
/* Handle full input blocks. */
} else if (n
== in
.dbsz
) {
in
.dbcnt
+= in
.dbrcnt
= n
;
/* Handle partial input blocks. */
} else if (n
!= in
.dbsz
) {
/* If sync, use the entire block. */
in
.dbcnt
+= in
.dbrcnt
= in
.dbsz
;
in
.dbcnt
+= in
.dbrcnt
= n
;
if ((n
= in
.dbcnt
) & 1) {
* POSIX states that if bs is set and no other conversions
* are specified, the block is output without buffering as
* Cleanup any remaining I/O and flush output. If necesssary, output file
else if (cfunc
== unblock
)
* Write one or more blocks out. The common case is writing a full
* output block in a single write; increment the full block stats.
* Otherwise, we're into partial block writes. If a partial write,
* and it's a character device, just warn. If a tape device, quit.
* The partial writes represent two cases. 1: Where the input block
* was less than expected so the output block was less than expected.
* 2: Where the input block was the right size but we were forced to
* write the block in multiple chunks. The original versions of dd(1)
* never wrote a block in more than a single write, so the latter case
* One special case is if we're forced to do the write -- in that case
* we play games with the buffer size, and it's usually a partial write.
for (n
= force
? out
.dbcnt
: out
.dbsz
;; n
= out
.dbsz
) {
for (cnt
= n
;; cnt
-= nw
) {
outp
+= nw
= write(out
.fd
, outp
, cnt
);
err("%s: %s", out
.name
, strerror(errno
));
if (out
.flags
& ISCHR
&& !warned
) {
warn("%s: short write on character device",
err("%s: short write on tape device", out
.name
);
if ((out
.dbcnt
-= n
) < out
.dbsz
)
/* Reassemble the output block. */
bcopy(out
.dbp
- out
.dbcnt
, out
.db
, out
.dbcnt
);
out
.dbp
= out
.db
+ out
.dbcnt
;