Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:24:19

0001 /*-
0002  * Copyright (c) 1991, 1993, 1994
0003  *  The Regents of the University of California.  All rights reserved.
0004  *
0005  * This code is derived from software contributed to Berkeley by
0006  * Keith Muller of the University of California, San Diego and Lance
0007  * Visser of Convex Computer Corporation.
0008  *
0009  * Redistribution and use in source and binary forms, with or without
0010  * modification, are permitted provided that the following conditions
0011  * are met:
0012  * 1. Redistributions of source code must retain the above copyright
0013  *    notice, this list of conditions and the following disclaimer.
0014  * 2. Redistributions in binary form must reproduce the above copyright
0015  *    notice, this list of conditions and the following disclaimer in the
0016  *    documentation and/or other materials provided with the distribution.
0017  * 4. Neither the name of the University nor the names of its contributors
0018  *    may be used to endorse or promote products derived from this software
0019  *    without specific prior written permission.
0020  *
0021  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
0022  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0023  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0024  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
0025  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
0026  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
0027  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
0028  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
0029  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
0030  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
0031  * SUCH DAMAGE.
0032  */
0033 
0034 #define _GNU_SOURCE
0035 
0036 #ifdef HAVE_CONFIG_H
0037 #include "config.h"
0038 #endif
0039 
0040 #if 0
0041 #ifndef lint
0042 static char const copyright[] =
0043 "@(#) Copyright (c) 1991, 1993, 1994\n\
0044     The Regents of the University of California.  All rights reserved.\n";
0045 #endif /* not lint */
0046 
0047 #ifndef lint
0048 static char sccsid[] = "@(#)dd.c    8.5 (Berkeley) 4/2/94";
0049 #endif /* not lint */
0050 #include <sys/cdefs.h>
0051 __FBSDID("$FreeBSD: src/bin/dd/dd.c,v 1.43 2004/08/15 19:10:05 rwatson Exp $");
0052 #endif
0053 
0054 #include <rtems.h>
0055 #include <rtems/shell.h>
0056 #include <rtems/shellconfig.h>
0057 
0058 #include <sys/param.h>
0059 #include <sys/stat.h>
0060 #if RTEMS_REMOVED
0061 #include <sys/conf.h>
0062 #include <sys/disklabel.h>
0063 #endif
0064 #include <sys/filio.h>
0065 #include <sys/time.h>
0066 
0067 #include <ctype.h>
0068 #include "err.h"
0069 #include <errno.h>
0070 #include <fcntl.h>
0071 #include <inttypes.h>
0072 #include <locale.h>
0073 #include <stdio.h>
0074 #include <stdlib.h>
0075 #include <string.h>
0076 #include <unistd.h>
0077 
0078 #include "dd.h"
0079 #include "extern-dd.h"
0080 
0081 #ifndef __unused
0082 #define __unused RTEMS_UNUSED
0083 #endif
0084 
0085 #define DD_DEFFILEMODE 0
0086 
0087 static void dd_close(rtems_shell_dd_globals* globals);
0088 static void dd_in(rtems_shell_dd_globals* globals);
0089 static void getfdtype(rtems_shell_dd_globals* globals, rtems_shell_dd_IO *);
0090 static void setup(rtems_shell_dd_globals* globals);
0091 
0092 #if RTEMS_REMOVED
0093 IO  in, out;        /* input/output state */
0094 STAT    st;         /* statistics */
0095 void    (*cfunc)(void);     /* conversion function */
0096 uintmax_t cpy_cnt;      /* # of blocks to copy */
0097 static off_t    pending = 0;    /* pending seek if sparse */
0098 u_int   ddflags = 0;        /* conversion options */
0099 size_t  cbsz;           /* conversion block size */
0100 uintmax_t files_cnt = 1;    /* # of files to copy */
0101 const   u_char *ctab;       /* conversion table */
0102 char    fill_char;      /* Character to fill with if defined */
0103 #endif
0104 
0105 static off_t    pending = 0;    /* pending seek if sparse */
0106 
0107 void
0108 rtems_shell_dd_exit (rtems_shell_dd_globals* globals, int code)
0109 {
0110   globals->exit_code = code;
0111   longjmp (globals->exit_jmp, 1);
0112 }
0113 
0114 static int main_dd(rtems_shell_dd_globals* globals, int argc, char *argv[]);
0115 
0116 static int
0117 rtems_shell_main_dd(int argc, char *argv[])
0118 {
0119   rtems_shell_dd_globals  dd_globals;
0120   rtems_shell_dd_globals* globals = &dd_globals;
0121   memset (globals, 0, sizeof (dd_globals));
0122   pending = 0;
0123   ddflags = 0;
0124   files_cnt = 1;
0125   dd_globals.exit_code = 1;
0126   if (setjmp (dd_globals.exit_jmp) == 0)
0127     dd_globals.exit_code = main_dd (globals, argc, argv);
0128   if (in.fd)
0129     close(in.fd);
0130   if (out.fd)
0131     close(out.fd);
0132   if (in.name)
0133     free((void*)in.name);
0134   if (out.name)
0135     free((void*)out.name);
0136   if (in.db)
0137     free(in.db);
0138   if (out.db && (in.db != out.db))
0139     free(out.db);
0140   return dd_globals.exit_code;
0141 }
0142 
0143 int
0144 main_dd(rtems_shell_dd_globals* globals, int argc __unused, char *argv[])
0145 {
0146     (void)setlocale(LC_CTYPE, "");
0147     jcl(globals, argv);
0148     setup(globals);
0149 
0150 #if RTEMS_REMOVED
0151     (void)signal(SIGINFO, summaryx);
0152     (void)signal(SIGINT, terminate);
0153 
0154     atexit(summary);
0155 #endif
0156 
0157     while (files_cnt--)
0158         dd_in(globals);
0159 
0160     dd_close(globals);
0161     exit(0);
0162     /* NOTREACHED */
0163     return 0;
0164 }
0165 
0166 static int
0167 parity(u_char c)
0168 {
0169     int i;
0170 
0171     i = c ^ (c >> 1) ^ (c >> 2) ^ (c >> 3) ^
0172         (c >> 4) ^ (c >> 5) ^ (c >> 6) ^ (c >> 7);
0173     return (i & 1);
0174 }
0175 
0176 static void
0177 setup(rtems_shell_dd_globals* globals)
0178 {
0179     u_int cnt;
0180     struct timeval tv;
0181 
0182     if (in.name == NULL) {
0183         in.name = "stdin";
0184         in.fd = STDIN_FILENO;
0185     } else {
0186         in.fd = open(in.name, O_RDONLY, 0);
0187         if (in.fd == -1)
0188             err(exit_jump, 1, "%s", in.name);
0189     }
0190 
0191     getfdtype(globals, &in);
0192 
0193     if (files_cnt > 1 && !(in.flags & ISTAPE))
0194         errx(exit_jump, 1, "files is not supported for non-tape devices");
0195 
0196     if (out.name == NULL) {
0197         /* No way to check for read access here. */
0198         out.fd = STDOUT_FILENO;
0199         out.name = "stdout";
0200     } else {
0201 #define OFLAGS \
0202     (O_CREAT | (ddflags & (C_SEEK | C_NOTRUNC) ? 0 : O_TRUNC))
0203         out.fd = open(out.name, O_RDWR | OFLAGS, DD_DEFFILEMODE);
0204         /*
0205          * May not have read access, so try again with write only.
0206          * Without read we may have a problem if output also does
0207          * not support seeks.
0208          */
0209         if (out.fd == -1) {
0210             out.fd = open(out.name, O_WRONLY | OFLAGS, DD_DEFFILEMODE);
0211             out.flags |= NOREAD;
0212         }
0213         if (out.fd == -1)
0214             err(exit_jump, 1, "%s", out.name);
0215     }
0216 
0217     getfdtype(globals, &out);
0218 
0219     /*
0220      * Allocate space for the input and output buffers.  If not doing
0221      * record oriented I/O, only need a single buffer.
0222      */
0223     if (!(ddflags & (C_BLOCK | C_UNBLOCK))) {
0224         if ((in.db = malloc(out.dbsz + in.dbsz - 1)) == NULL)
0225             err(exit_jump, 1, "input buffer");
0226         out.db = in.db;
0227     } else if ((in.db = malloc(MAX(in.dbsz, cbsz) + cbsz)) == NULL ||
0228         (out.db = malloc(out.dbsz + cbsz)) == NULL)
0229         err(exit_jump, 1, "output buffer");
0230     in.dbp = in.db;
0231     out.dbp = out.db;
0232 
0233     /* Position the input/output streams. */
0234     if (in.offset)
0235         pos_in(globals);
0236     if (out.offset)
0237         pos_out(globals);
0238 
0239     /*
0240      * Truncate the output file.  If it fails on a type of output file
0241      * that it should _not_ fail on, error out.
0242      */
0243     if ((ddflags & (C_OF | C_SEEK | C_NOTRUNC)) == (C_OF | C_SEEK) &&
0244         out.flags & ISTRUNC)
0245         if (ftruncate(out.fd, out.offset * out.dbsz) == -1)
0246             err(exit_jump, 1, "truncating %s", out.name);
0247 
0248     if (ddflags & (C_LCASE  | C_UCASE | C_ASCII | C_EBCDIC | C_PARITY)) {
0249         if (ctab != NULL) {
0250             for (cnt = 0; cnt <= 0377; ++cnt)
0251                 casetab[cnt] = ctab[cnt];
0252         } else {
0253             for (cnt = 0; cnt <= 0377; ++cnt)
0254                 casetab[cnt] = cnt;
0255         }
0256         if ((ddflags & C_PARITY) && !(ddflags & C_ASCII)) {
0257             /*
0258              * If the input is not EBCDIC, and we do parity
0259              * processing, strip input parity.
0260              */
0261             for (cnt = 200; cnt <= 0377; ++cnt)
0262                 casetab[cnt] = casetab[cnt & 0x7f];
0263         }
0264         if (ddflags & C_LCASE) {
0265             for (cnt = 0; cnt <= 0377; ++cnt)
0266                 casetab[cnt] = tolower(casetab[cnt]);
0267         } else if (ddflags & C_UCASE) {
0268             for (cnt = 0; cnt <= 0377; ++cnt)
0269                 casetab[cnt] = toupper(casetab[cnt]);
0270         }
0271         if ((ddflags & C_PARITY)) {
0272             /*
0273              * This should strictly speaking be a no-op, but I
0274              * wonder what funny LANG settings could get us.
0275              */
0276             for (cnt = 0; cnt <= 0377; ++cnt)
0277                 casetab[cnt] = casetab[cnt] & 0x7f;
0278         }
0279         if ((ddflags & C_PARSET)) {
0280             for (cnt = 0; cnt <= 0377; ++cnt)
0281                 casetab[cnt] = casetab[cnt] | 0x80;
0282         }
0283         if ((ddflags & C_PAREVEN)) {
0284             for (cnt = 0; cnt <= 0377; ++cnt)
0285                 if (parity(casetab[cnt]))
0286                     casetab[cnt] = casetab[cnt] | 0x80;
0287         }
0288         if ((ddflags & C_PARODD)) {
0289             for (cnt = 0; cnt <= 0377; ++cnt)
0290                 if (!parity(casetab[cnt]))
0291                     casetab[cnt] = casetab[cnt] | 0x80;
0292         }
0293 
0294         ctab = casetab;
0295     }
0296 
0297     (void)gettimeofday(&tv, (struct timezone *)NULL);
0298     st.start = tv.tv_sec + tv.tv_usec * 1e-6;
0299 }
0300 
0301 static void
0302 getfdtype(rtems_shell_dd_globals* globals, rtems_shell_dd_IO *io)
0303 {
0304     struct stat sb;
0305 #if RTEMS_REMOVED
0306     int type;
0307 #endif
0308 
0309     if (fstat(io->fd, &sb) == -1)
0310         err(exit_jump, 1, "%s", io->name);
0311     if (S_ISREG(sb.st_mode))
0312         io->flags |= ISTRUNC;
0313 #if RTEMS_REMOVED
0314     if (S_ISCHR(sb.st_mode) || S_ISBLK(sb.st_mode)) {
0315         if (ioctl(io->fd, FIODTYPE, &type) == -1) {
0316             err(exit_jump, 1, "%s", io->name);
0317         } else {
0318             if (type & D_TAPE)
0319                 io->flags |= ISTAPE;
0320             else if (type & (D_DISK | D_MEM))
0321                 io->flags |= ISSEEK;
0322             if (S_ISCHR(sb.st_mode) && (type & D_TAPE) == 0)
0323                 io->flags |= ISCHR;
0324         }
0325         return;
0326     }
0327 #else
0328   io->flags |= ISSEEK;
0329 #endif
0330     errno = 0;
0331     if (lseek(io->fd, (off_t)0, SEEK_CUR) == -1 && errno == ESPIPE)
0332         io->flags |= ISPIPE;
0333     else
0334         io->flags |= ISSEEK;
0335 }
0336 
0337 static void
0338 swapbytes(void *v, size_t len)
0339 {
0340     unsigned char *p = v;
0341     unsigned char t;
0342 
0343     while (len > 1) {
0344         t = p[0];
0345         p[0] = p[1];
0346         p[1] = t;
0347         p += 2;
0348         len -= 2;
0349     }
0350 }
0351 
0352 static void
0353 dd_in(rtems_shell_dd_globals* globals)
0354 {
0355     ssize_t n;
0356 
0357     for (;;) {
0358         switch (cpy_cnt) {
0359         case -1:            /* count=0 was specified */
0360             return;
0361         case 0:
0362             break;
0363         default:
0364             if (st.in_full + st.in_part >= (uintmax_t)cpy_cnt)
0365                 return;
0366             break;
0367         }
0368 
0369         /*
0370          * Zero the buffer first if sync; if doing block operations,
0371          * use spaces.
0372          */
0373         if (ddflags & C_SYNC) {
0374             if (ddflags & C_FILL)
0375                 memset(in.dbp, fill_char, in.dbsz);
0376             else if (ddflags & (C_BLOCK | C_UNBLOCK))
0377                 memset(in.dbp, ' ', in.dbsz);
0378             else
0379                 memset(in.dbp, 0, in.dbsz);
0380         }
0381 
0382         n = read(in.fd, in.dbp, in.dbsz);
0383         if (n == 0) {
0384             in.dbrcnt = 0;
0385             return;
0386         }
0387 
0388         /* Read error. */
0389         if (n == -1) {
0390             /*
0391              * If noerror not specified, die.  POSIX requires that
0392              * the warning message be followed by an I/O display.
0393              */
0394             if (!(ddflags & C_NOERROR))
0395                 err(exit_jump, 1, "%s", in.name);
0396             warn("%s", in.name);
0397             summary(globals);
0398 
0399             /*
0400              * If it's a seekable file descriptor, seek past the
0401              * error.  If your OS doesn't do the right thing for
0402              * raw disks this section should be modified to re-read
0403              * in sector size chunks.
0404              */
0405             if (in.flags & ISSEEK &&
0406                 lseek(in.fd, (off_t)in.dbsz, SEEK_CUR))
0407                 warn("%s", in.name);
0408 
0409             /* If sync not specified, omit block and continue. */
0410             if (!(ddflags & C_SYNC))
0411                 continue;
0412 
0413             /* Read errors count as full blocks. */
0414             in.dbcnt += in.dbrcnt = in.dbsz;
0415             ++st.in_full;
0416 
0417         /* Handle full input blocks. */
0418         } else if ((size_t)n == in.dbsz) {
0419             in.dbcnt += in.dbrcnt = n;
0420             ++st.in_full;
0421 
0422         /* Handle partial input blocks. */
0423         } else {
0424             /* If sync, use the entire block. */
0425             if (ddflags & C_SYNC)
0426                 in.dbcnt += in.dbrcnt = in.dbsz;
0427             else
0428                 in.dbcnt += in.dbrcnt = n;
0429             ++st.in_part;
0430         }
0431 
0432         /*
0433          * POSIX states that if bs is set and no other conversions
0434          * than noerror, notrunc or sync are specified, the block
0435          * is output without buffering as it is read.
0436          */
0437         if (ddflags & C_BS) {
0438             out.dbcnt = in.dbcnt;
0439             dd_out(globals, 1);
0440             in.dbcnt = 0;
0441             continue;
0442         }
0443 
0444         if (ddflags & C_SWAB) {
0445             if ((n = in.dbrcnt) & 1) {
0446                 ++st.swab;
0447                 --n;
0448             }
0449             swapbytes(in.dbp, (size_t)n);
0450         }
0451 
0452         in.dbp += in.dbrcnt;
0453         (*cfunc)(globals);
0454     }
0455 }
0456 
0457 /*
0458  * Clean up any remaining I/O and flush output.  If necessary, the output file
0459  * is truncated.
0460  */
0461 static void
0462 dd_close(rtems_shell_dd_globals* globals)
0463 {
0464     if (cfunc == def)
0465         def_close(globals);
0466     else if (cfunc == block)
0467         block_close(globals);
0468     else if (cfunc == unblock)
0469         unblock_close(globals);
0470     if (ddflags & C_OSYNC && out.dbcnt && out.dbcnt < out.dbsz) {
0471         if (ddflags & C_FILL)
0472             memset(out.dbp, fill_char, out.dbsz - out.dbcnt);
0473         else if (ddflags & (C_BLOCK | C_UNBLOCK))
0474             memset(out.dbp, ' ', out.dbsz - out.dbcnt);
0475         else
0476             memset(out.dbp, 0, out.dbsz - out.dbcnt);
0477         out.dbcnt = out.dbsz;
0478     }
0479     if (out.dbcnt || pending)
0480         dd_out(globals, 1);
0481 }
0482 
0483 void
0484 dd_out(rtems_shell_dd_globals* globals, int force)
0485 {
0486     u_char *outp;
0487     size_t cnt, i, n;
0488     ssize_t nw;
0489     static int warned;
0490     int sparse;
0491 
0492     /*
0493      * Write one or more blocks out.  The common case is writing a full
0494      * output block in a single write; increment the full block stats.
0495      * Otherwise, we're into partial block writes.  If a partial write,
0496      * and it's a character device, just warn.  If a tape device, quit.
0497      *
0498      * The partial writes represent two cases.  1: Where the input block
0499      * was less than expected so the output block was less than expected.
0500      * 2: Where the input block was the right size but we were forced to
0501      * write the block in multiple chunks.  The original versions of dd(1)
0502      * never wrote a block in more than a single write, so the latter case
0503      * never happened.
0504      *
0505      * One special case is if we're forced to do the write -- in that case
0506      * we play games with the buffer size, and it's usually a partial write.
0507      */
0508     outp = out.db;
0509     for (n = force ? out.dbcnt : out.dbsz;; n = out.dbsz) {
0510         for (cnt = n;; cnt -= nw) {
0511             sparse = 0;
0512             if (ddflags & C_SPARSE) {
0513                 sparse = 1; /* Is buffer sparse? */
0514                 for (i = 0; i < cnt; i++)
0515                     if (outp[i] != 0) {
0516                         sparse = 0;
0517                         break;
0518                     }
0519             }
0520             if (sparse && !force) {
0521                 pending += cnt;
0522                 nw = cnt;
0523             } else {
0524                 if (pending != 0) {
0525                     if (force)
0526                         pending--;
0527                     if (lseek(out.fd, pending, SEEK_CUR) ==
0528                         -1)
0529                         err(exit_jump, 2, "%s: seek error creating sparse file",
0530                             out.name);
0531                     if (force)
0532                         write(out.fd, outp, 1);
0533                     pending = 0;
0534                 }
0535                 if (cnt)
0536                     nw = write(out.fd, outp, cnt);
0537                 else
0538                     return;
0539             }
0540 
0541             if (nw <= 0) {
0542                 if (nw == 0)
0543                     errx(exit_jump, 1, "%s: end of device", out.name);
0544                 if (errno != EINTR)
0545                     err(exit_jump, 1, "%s", out.name);
0546                 nw = 0;
0547             }
0548             outp += nw;
0549             st.bytes += nw;
0550             if ((size_t)nw == n) {
0551                 if (n != out.dbsz)
0552                     ++st.out_part;
0553                 else
0554                     ++st.out_full;
0555                 break;
0556             }
0557             ++st.out_part;
0558             if ((size_t)nw == cnt)
0559                 break;
0560             if (out.flags & ISTAPE)
0561                 errx(exit_jump, 1, "%s: short write on tape device",
0562                     out.name);
0563             if (out.flags & ISCHR && !warned) {
0564                 warned = 1;
0565                 warnx("%s: short write on character device",
0566                     out.name);
0567             }
0568         }
0569         if ((out.dbcnt -= n) < out.dbsz)
0570             break;
0571     }
0572 
0573     /* Reassemble the output block. */
0574     if (out.dbcnt)
0575         (void)memmove(out.db, out.dbp - out.dbcnt, out.dbcnt);
0576     out.dbp = out.db + out.dbcnt;
0577 }
0578 
0579 rtems_shell_cmd_t rtems_shell_DD_Command = {
0580   "dd",                                                /* name */
0581   "dd [OPERAND]...",                                   /* usage */
0582   "files",                                             /* topic */
0583   rtems_shell_main_dd,                                 /* command */
0584   NULL,                                                /* alias */
0585   NULL                                                 /* next */
0586 };