diff options
author | Cameron Katri <me@cameronkatri.com> | 2021-05-09 14:20:58 -0400 |
---|---|---|
committer | Cameron Katri <me@cameronkatri.com> | 2021-05-09 14:20:58 -0400 |
commit | 5fd83771641d15c418f747bd343ba6738d3875f7 (patch) | |
tree | 5abf0f78f680d9837dbd93d4d4c3933bb7509599 /file_cmds/dd/dd.c | |
download | apple_cmds-5fd83771641d15c418f747bd343ba6738d3875f7.tar.gz apple_cmds-5fd83771641d15c418f747bd343ba6738d3875f7.tar.zst apple_cmds-5fd83771641d15c418f747bd343ba6738d3875f7.zip |
Import macOS userland
adv_cmds-176
basic_cmds-55
bootstrap_cmds-116.100.1
developer_cmds-66
diskdev_cmds-667.40.1
doc_cmds-53.60.1
file_cmds-321.40.3
mail_cmds-35
misc_cmds-34
network_cmds-606.40.1
patch_cmds-17
remote_cmds-63
shell_cmds-216.60.1
system_cmds-880.60.2
text_cmds-106
Diffstat (limited to 'file_cmds/dd/dd.c')
-rw-r--r-- | file_cmds/dd/dd.c | 481 |
1 files changed, 481 insertions, 0 deletions
diff --git a/file_cmds/dd/dd.c b/file_cmds/dd/dd.c new file mode 100644 index 0000000..5731186 --- /dev/null +++ b/file_cmds/dd/dd.c @@ -0,0 +1,481 @@ +/*- + * Copyright (c) 1991, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * 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. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifndef lint +__used static char const copyright[] = +"@(#) Copyright (c) 1991, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)dd.c 8.5 (Berkeley) 4/2/94"; +#endif +__used static const char rcsid[] = + "$FreeBSD: src/bin/dd/dd.c,v 1.36 2002/03/07 14:00:33 markm Exp $"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/conf.h> +#include <sys/filio.h> +#include <sys/time.h> + +#ifdef __APPLE__ +#include <sys/ioctl.h> +#else +#include <sys/disklabel.h> +#endif + +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <locale.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "dd.h" +#include "extern.h" + +static void dd_close(void); +static void dd_in(void); +static void getfdtype(IO *); +static void setup(void); + +IO in, out; /* input/output state */ +STAT st; /* statistics */ +void (*cfunc)(void); /* conversion function */ +quad_t cpy_cnt; /* # of blocks to copy */ +off_t pending = 0; /* pending seek if sparse */ +u_int ddflags; /* conversion options */ +size_t cbsz; /* conversion block size */ +quad_t files_cnt = 1; /* # of files to copy */ +const u_char *ctab; /* conversion table */ + +int +main(int argc, char *argv[]) +{ + (void)setlocale(LC_CTYPE, ""); + jcl(argv); + setup(); + + (void)signal(SIGINFO, summaryx); + (void)signal(SIGINT, terminate); + + atexit(summary); + + while (files_cnt--) + dd_in(); + + dd_close(); + exit(0); +} + +static void +setup(void) +{ + u_int cnt; + struct timeval tv; + + if (in.name == NULL) { + in.name = "stdin"; + in.fd = STDIN_FILENO; + } else { + in.fd = open(in.name, O_RDONLY, 0); + if (in.fd == -1) + err(1, "%s", in.name); + } + + getfdtype(&in); + + if (files_cnt > 1 && !(in.flags & ISTAPE)) + errx(1, "files is not supported for non-tape devices"); + + if (out.name == NULL) { + /* No way to check for read access here. */ + out.fd = STDOUT_FILENO; + out.name = "stdout"; + } else { +#define OFLAGS \ + (O_CREAT | (ddflags & (C_SEEK | C_NOTRUNC) ? 0 : O_TRUNC)) + out.fd = open(out.name, O_RDWR | OFLAGS, DEFFILEMODE); + /* + * May not have read access, so try again with write only. + * Without read we may have a problem if output also does + * not support seeks. + */ + if (out.fd == -1) { + out.fd = open(out.name, O_WRONLY | OFLAGS, DEFFILEMODE); + out.flags |= NOREAD; + } + if (out.fd == -1) + err(1, "%s", out.name); + } + + getfdtype(&out); + + /* + * 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))) { + if ((in.db = malloc(out.dbsz + in.dbsz - 1)) == NULL) + err(1, "input buffer"); + out.db = in.db; + } else if ((in.db = malloc(MAX(in.dbsz, cbsz) + cbsz)) == NULL || + (out.db = malloc(out.dbsz + cbsz)) == NULL) + err(1, "output buffer"); + in.dbp = in.db; + out.dbp = out.db; + + /* Position the input/output streams. */ + if (in.offset) + pos_in(); + if (out.offset) + pos_out(); + + /* + * Truncate the output file. If it fails on a type of output file + * that it should _not_ fail on, error out. + */ + if ((ddflags & (C_OF | C_SEEK | C_NOTRUNC)) == (C_OF | C_SEEK) && + out.flags & ISTRUNC) + if (ftruncate(out.fd, out.offset * out.dbsz) == -1) + err(1, "truncating %s", out.name); + + /* + * If converting case at the same time as another conversion, build a + * table that does both at once. If just converting case, use the + * built-in tables. + */ + if (ddflags & (C_LCASE | C_UCASE)) { + if (ddflags & (C_ASCII | C_EBCDIC)) { + if (ddflags & C_LCASE) { + for (cnt = 0; cnt <= 0377; ++cnt) + casetab[cnt] = tolower(ctab[cnt]); + } else { + for (cnt = 0; cnt <= 0377; ++cnt) + casetab[cnt] = toupper(ctab[cnt]); + } + } else { + if (ddflags & C_LCASE) { + for (cnt = 0; cnt <= 0377; ++cnt) + casetab[cnt] = tolower((int)cnt); + } else { + for (cnt = 0; cnt <= 0377; ++cnt) + casetab[cnt] = toupper((int)cnt); + } + } + ctab = casetab; + } + + (void)gettimeofday(&tv, (struct timezone *)NULL); + st.start = tv.tv_sec + tv.tv_usec * 1e-6; +} + +static void +getfdtype(IO *io) +{ + struct stat sb; + int type; + + if (fstat(io->fd, &sb) == -1) + err(1, "%s", io->name); + if (S_ISREG(sb.st_mode)) + io->flags |= ISTRUNC; + if (S_ISCHR(sb.st_mode) || S_ISBLK(sb.st_mode)) { + if (ioctl(io->fd, FIODTYPE, &type) == -1) { + err(1, "%s", io->name); + } else { +#ifdef __APPLE__ /* MacOSX uses enumeration for type not a bitmask */ + if (type == D_TAPE) + io->flags |= ISTAPE; + else if (type == D_DISK || type == D_TTY) { +#else /* !__APPLE__ */ + if (type & D_TAPE) + io->flags |= ISTAPE; + else if (type & (D_DISK | D_MEM)) { + if (type & D_DISK) { + const int one = 1; + + (void)ioctl(io->fd, DIOCWLABEL, &one); + } +#endif /* __APPLE__ */ + io->flags |= ISSEEK; + } +#ifdef __APPLE__ + if (S_ISCHR(sb.st_mode) && (type != D_TAPE)) +#else /* !__APPLE__ */ + if (S_ISCHR(sb.st_mode) && (type & D_TAPE) == 0) +#endif /* __APPLE__ */ + io->flags |= ISCHR; + } + return; + } + errno = 0; + if (lseek(io->fd, (off_t)0, SEEK_CUR) == -1 && errno == ESPIPE) + io->flags |= ISPIPE; + else + io->flags |= ISSEEK; +} + +static void +dd_in(void) +{ + ssize_t n; + + for (;;) { + switch (cpy_cnt) { + case -1: /* count=0 was specified */ + return; + case 0: + break; + default: + if (st.in_full + st.in_part >= (u_quad_t)cpy_cnt) + return; + break; + } + + /* + * Zero the buffer first if sync; if doing block operations, + * use spaces. + */ + if (ddflags & C_SYNC) { + if (ddflags & (C_BLOCK | C_UNBLOCK)) + memset(in.dbp, ' ', in.dbsz); + else + memset(in.dbp, 0, in.dbsz); + } + + n = read(in.fd, in.dbp, in.dbsz); + if (n == 0) { + in.dbrcnt = 0; + return; + } + + /* Read error. */ + if (n == -1) { + /* + * If noerror not specified, die. POSIX requires that + * the warning message be followed by an I/O display. + */ + if (!(ddflags & C_NOERROR)) + err(1, "%s", in.name); + warn("%s", in.name); + summary(); + + /* + * If it's a seekable file descriptor, seek past the + * error. If your OS doesn't do the right thing for + * raw disks this section should be modified to re-read + * in sector size chunks. + */ + if (in.flags & ISSEEK && + lseek(in.fd, (off_t)in.dbsz, SEEK_CUR)) + warn("%s", in.name); + + /* If sync not specified, omit block and continue. */ + if (!(ddflags & C_SYNC)) + continue; + + /* Read errors count as full blocks. */ + in.dbcnt += in.dbrcnt = in.dbsz; + ++st.in_full; + + /* Handle full input blocks. */ + } else if ((size_t)n == in.dbsz) { + in.dbcnt += in.dbrcnt = n; + ++st.in_full; + + /* Handle partial input blocks. */ + } else { + /* If sync, use the entire block. */ + if (ddflags & C_SYNC) + in.dbcnt += in.dbrcnt = in.dbsz; + else + in.dbcnt += in.dbrcnt = n; + ++st.in_part; + } + + /* + * POSIX states that if bs is set and no other conversions + * than noerror, notrunc or sync are specified, the block + * is output without buffering as it is read. + */ + if (ddflags & C_BS) { + out.dbcnt = in.dbcnt; + dd_out(1); + in.dbcnt = 0; + continue; + } + + if (ddflags & C_SWAB) { + if ((n = in.dbrcnt) & 1) { + ++st.swab; + --n; + } + swab(in.dbp, in.dbp, (size_t)n); + } + + in.dbp += in.dbrcnt; + (*cfunc)(); + } +} + +/* + * Clean up any remaining I/O and flush output. If necessary, the output file + * is truncated. + */ +static void +dd_close(void) +{ + if (cfunc == def) + def_close(); + else if (cfunc == block) + block_close(); + else if (cfunc == unblock) + unblock_close(); + if (ddflags & C_OSYNC && out.dbcnt && out.dbcnt < out.dbsz) { + if (ddflags & (C_BLOCK | C_UNBLOCK)) + memset(out.dbp, ' ', out.dbsz - out.dbcnt); + else + memset(out.dbp, 0, out.dbsz - out.dbcnt); + out.dbcnt = out.dbsz; + } + if (out.dbcnt || pending) + dd_out(1); +} + +void +dd_out(int force) +{ + u_char *outp; + size_t cnt, i, n; + ssize_t nw; + static int warned; + int sparse; + + /* + * 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 + * never happened. + * + * 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. + */ + outp = out.db; + for (n = force ? out.dbcnt : out.dbsz;; n = out.dbsz) { + for (cnt = n;; cnt -= nw) { + sparse = 0; + if (ddflags & C_SPARSE) { + sparse = 1; /* Is buffer sparse? */ + for (i = 0; i < cnt; i++) + if (outp[i] != 0) { + sparse = 0; + break; + } + } + if (sparse && !force) { + pending += cnt; + nw = cnt; + } else { + if (pending != 0) { + if (force) + pending--; + if (lseek(out.fd, pending, SEEK_CUR) == + -1) + err(2, "%s: seek error creating sparse file", + out.name); + if (force) + write(out.fd, outp, 1); + pending = 0; + } + if (cnt) + nw = write(out.fd, outp, cnt); + else + return; + } + + if (nw <= 0) { + if (nw == 0) + errx(1, "%s: end of device", out.name); + if (errno != EINTR) + err(1, "%s", out.name); + nw = 0; + } + outp += nw; + st.bytes += nw; + if ((size_t)nw == n) { + if (n != out.dbsz) + ++st.out_part; + else + ++st.out_full; + break; + } + ++st.out_part; + if ((size_t)nw == cnt) + break; + if (out.flags & ISTAPE) + errx(1, "%s: short write on tape device", + out.name); + if (out.flags & ISCHR && !warned) { + warned = 1; + warnx("%s: short write on character device", + out.name); + } + } + if ((out.dbcnt -= n) < out.dbsz) + break; + } + + /* Reassemble the output block. */ + if (out.dbcnt) + (void)memmove(out.db, out.dbp - out.dbcnt, out.dbcnt); + out.dbp = out.db + out.dbcnt; +} |