source: rtems/cpukit/libmisc/shell/main_dd.c @ 0893220

4.104.115
Last change on this file since 0893220 was 0893220, checked in by Ralf Corsepius <ralf.corsepius@…>, on Nov 29, 2009 at 12:12:39 PM

Whitespace removal.

  • Property mode set to 100644
File size: 14.4 KB
Line 
1/*-
2 * Copyright (c) 1991, 1993, 1994
3 *      The Regents of the University of California.  All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Keith Muller of the University of California, San Diego and Lance
7 * Visser of Convex Computer Corporation.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 * 4. Neither the name of the University nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#if 0
35#ifndef lint
36static char const copyright[] =
37"@(#) Copyright (c) 1991, 1993, 1994\n\
38        The Regents of the University of California.  All rights reserved.\n";
39#endif /* not lint */
40
41#ifndef lint
42static char sccsid[] = "@(#)dd.c        8.5 (Berkeley) 4/2/94";
43#endif /* not lint */
44#include <sys/cdefs.h>
45__FBSDID("$FreeBSD: src/bin/dd/dd.c,v 1.43 2004/08/15 19:10:05 rwatson Exp $");
46#endif
47
48#include <rtems.h>
49#include <rtems/shell.h>
50#include <rtems/shellconfig.h>
51
52#include <sys/param.h>
53#include <sys/stat.h>
54#if RTEMS_REMOVED
55#include <sys/conf.h>
56#include <sys/disklabel.h>
57#endif
58#include <sys/filio.h>
59#include <sys/time.h>
60
61#include <ctype.h>
62#include <err.h>
63#include <errno.h>
64#include <fcntl.h>
65#include <inttypes.h>
66#include <locale.h>
67#include <stdio.h>
68#include <stdlib.h>
69#include <string.h>
70#include <unistd.h>
71
72#include "dd.h"
73#include "extern-dd.h"
74
75#define DEFFILEMODE 0
76
77static void dd_close(rtems_shell_dd_globals* globals);
78static void dd_in(rtems_shell_dd_globals* globals);
79static void getfdtype(rtems_shell_dd_globals* globals, IO *);
80static void setup(rtems_shell_dd_globals* globals);
81
82#if RTEMS_REMOVED
83IO      in, out;                /* input/output state */
84STAT    st;                     /* statistics */
85void    (*cfunc)(void);         /* conversion function */
86uintmax_t cpy_cnt;              /* # of blocks to copy */
87static off_t    pending = 0;    /* pending seek if sparse */
88u_int   ddflags = 0;            /* conversion options */
89size_t  cbsz;                   /* conversion block size */
90uintmax_t files_cnt = 1;        /* # of files to copy */
91const   u_char *ctab;           /* conversion table */
92char    fill_char;              /* Character to fill with if defined */
93#endif
94
95static off_t    pending = 0;    /* pending seek if sparse */
96
97void
98rtems_shell_dd_exit (rtems_shell_dd_globals* globals, int code)
99{
100  globals->exit_code = code;
101  longjmp (globals->exit_jmp, 1);
102}
103
104static int main_dd(rtems_shell_dd_globals* globals, int argc, char *argv[]);
105
106int
107rtems_shell_main_dd(int argc, char *argv[])
108{
109  rtems_shell_dd_globals  dd_globals;
110  rtems_shell_dd_globals* globals = &dd_globals;
111  memset (globals, 0, sizeof (dd_globals));
112  pending = 0;
113  ddflags = 0;
114  files_cnt = 1;
115  dd_globals.exit_code = 1;
116  if (setjmp (dd_globals.exit_jmp) == 0)
117    dd_globals.exit_code = main_dd (globals, argc, argv);
118  if (in.fd)
119    close(in.fd);
120  if (out.fd)
121    close(out.fd);
122  if (in.name)
123    free((void*)in.name);
124  if (out.name)
125    free((void*)out.name);
126  if (in.db)
127    free(in.db);
128  if (out.db && (in.db != out.db))
129    free(out.db);
130  return dd_globals.exit_code;
131}
132
133int
134main_dd(rtems_shell_dd_globals* globals, int argc __unused, char *argv[])
135{
136        (void)setlocale(LC_CTYPE, "");
137        jcl(globals, argv);
138        setup(globals);
139
140#if RTEMS_REMOVED
141        (void)signal(SIGINFO, summaryx);
142        (void)signal(SIGINT, terminate);
143
144        atexit(summary);
145#endif
146
147        while (files_cnt--)
148                dd_in(globals);
149
150        dd_close(globals);
151        exit(0);
152        /* NOTREACHED */
153        return 0;
154}
155
156static int
157parity(u_char c)
158{
159        int i;
160
161        i = c ^ (c >> 1) ^ (c >> 2) ^ (c >> 3) ^
162            (c >> 4) ^ (c >> 5) ^ (c >> 6) ^ (c >> 7);
163        return (i & 1);
164}
165
166static void
167setup(rtems_shell_dd_globals* globals)
168{
169        u_int cnt;
170        struct timeval tv;
171
172        if (in.name == NULL) {
173                in.name = "stdin";
174                in.fd = STDIN_FILENO;
175        } else {
176                in.fd = open(in.name, O_RDONLY, 0);
177                if (in.fd == -1)
178                        err(exit_jump, 1, "%s", in.name);
179        }
180
181        getfdtype(globals, &in);
182
183        if (files_cnt > 1 && !(in.flags & ISTAPE))
184                errx(exit_jump, 1, "files is not supported for non-tape devices");
185
186        if (out.name == NULL) {
187                /* No way to check for read access here. */
188                out.fd = STDOUT_FILENO;
189                out.name = "stdout";
190        } else {
191#define OFLAGS \
192    (O_CREAT | (ddflags & (C_SEEK | C_NOTRUNC) ? 0 : O_TRUNC))
193                out.fd = open(out.name, O_RDWR | OFLAGS, DEFFILEMODE);
194                /*
195                 * May not have read access, so try again with write only.
196                 * Without read we may have a problem if output also does
197                 * not support seeks.
198                 */
199                if (out.fd == -1) {
200                        out.fd = open(out.name, O_WRONLY | OFLAGS, DEFFILEMODE);
201                        out.flags |= NOREAD;
202                }
203                if (out.fd == -1)
204                        err(exit_jump, 1, "%s", out.name);
205        }
206
207        getfdtype(globals, &out);
208
209        /*
210         * Allocate space for the input and output buffers.  If not doing
211         * record oriented I/O, only need a single buffer.
212         */
213        if (!(ddflags & (C_BLOCK | C_UNBLOCK))) {
214                if ((in.db = malloc(out.dbsz + in.dbsz - 1)) == NULL)
215                        err(exit_jump, 1, "input buffer");
216                out.db = in.db;
217        } else if ((in.db = malloc(MAX(in.dbsz, cbsz) + cbsz)) == NULL ||
218            (out.db = malloc(out.dbsz + cbsz)) == NULL)
219                err(exit_jump, 1, "output buffer");
220        in.dbp = in.db;
221        out.dbp = out.db;
222
223        /* Position the input/output streams. */
224        if (in.offset)
225                pos_in(globals);
226        if (out.offset)
227                pos_out(globals);
228
229        /*
230         * Truncate the output file.  If it fails on a type of output file
231         * that it should _not_ fail on, error out.
232         */
233        if ((ddflags & (C_OF | C_SEEK | C_NOTRUNC)) == (C_OF | C_SEEK) &&
234            out.flags & ISTRUNC)
235                if (ftruncate(out.fd, out.offset * out.dbsz) == -1)
236                        err(exit_jump, 1, "truncating %s", out.name);
237
238        if (ddflags & (C_LCASE  | C_UCASE | C_ASCII | C_EBCDIC | C_PARITY)) {
239                if (ctab != NULL) {
240                        for (cnt = 0; cnt <= 0377; ++cnt)
241                                casetab[cnt] = ctab[cnt];
242                } else {
243                        for (cnt = 0; cnt <= 0377; ++cnt)
244                                casetab[cnt] = cnt;
245                }
246                if ((ddflags & C_PARITY) && !(ddflags & C_ASCII)) {
247                        /*
248                         * If the input is not EBCDIC, and we do parity
249                         * processing, strip input parity.
250                         */
251                        for (cnt = 200; cnt <= 0377; ++cnt)
252                                casetab[cnt] = casetab[cnt & 0x7f];
253                }
254                if (ddflags & C_LCASE) {
255                        for (cnt = 0; cnt <= 0377; ++cnt)
256                                casetab[cnt] = tolower(casetab[cnt]);
257                } else if (ddflags & C_UCASE) {
258                        for (cnt = 0; cnt <= 0377; ++cnt)
259                                casetab[cnt] = toupper(casetab[cnt]);
260                }
261                if ((ddflags & C_PARITY)) {
262                        /*
263                         * This should strictly speaking be a no-op, but I
264                         * wonder what funny LANG settings could get us.
265                         */
266                        for (cnt = 0; cnt <= 0377; ++cnt)
267                                casetab[cnt] = casetab[cnt] & 0x7f;
268                }
269                if ((ddflags & C_PARSET)) {
270                        for (cnt = 0; cnt <= 0377; ++cnt)
271                                casetab[cnt] = casetab[cnt] | 0x80;
272                }
273                if ((ddflags & C_PAREVEN)) {
274                        for (cnt = 0; cnt <= 0377; ++cnt)
275                                if (parity(casetab[cnt]))
276                                        casetab[cnt] = casetab[cnt] | 0x80;
277                }
278                if ((ddflags & C_PARODD)) {
279                        for (cnt = 0; cnt <= 0377; ++cnt)
280                                if (!parity(casetab[cnt]))
281                                        casetab[cnt] = casetab[cnt] | 0x80;
282                }
283
284                ctab = casetab;
285        }
286
287        (void)gettimeofday(&tv, (struct timezone *)NULL);
288        st.start = tv.tv_sec + tv.tv_usec * 1e-6;
289}
290
291static void
292getfdtype(rtems_shell_dd_globals* globals, IO *io)
293{
294        struct stat sb;
295#if RTEMS_REMOVED
296        int type;
297#endif
298
299        if (fstat(io->fd, &sb) == -1)
300                err(exit_jump, 1, "%s", io->name);
301        if (S_ISREG(sb.st_mode))
302                io->flags |= ISTRUNC;
303#if RTEMS_REMOVED
304        if (S_ISCHR(sb.st_mode) || S_ISBLK(sb.st_mode)) {
305                if (ioctl(io->fd, FIODTYPE, &type) == -1) {
306                        err(exit_jump, 1, "%s", io->name);
307                } else {
308                        if (type & D_TAPE)
309                                io->flags |= ISTAPE;
310                        else if (type & (D_DISK | D_MEM))
311                                io->flags |= ISSEEK;
312                        if (S_ISCHR(sb.st_mode) && (type & D_TAPE) == 0)
313                                io->flags |= ISCHR;
314                }
315                return;
316        }
317#else
318  io->flags |= ISSEEK;
319#endif
320        errno = 0;
321        if (lseek(io->fd, (off_t)0, SEEK_CUR) == -1 && errno == ESPIPE)
322                io->flags |= ISPIPE;
323        else
324                io->flags |= ISSEEK;
325}
326
327static void
328dd_in(rtems_shell_dd_globals* globals)
329{
330        ssize_t n;
331
332        for (;;) {
333                switch (cpy_cnt) {
334                case -1:                        /* count=0 was specified */
335                        return;
336                case 0:
337                        break;
338                default:
339                        if (st.in_full + st.in_part >= (uintmax_t)cpy_cnt)
340                                return;
341                        break;
342                }
343
344                /*
345                 * Zero the buffer first if sync; if doing block operations,
346                 * use spaces.
347                 */
348                if (ddflags & C_SYNC) {
349                        if (ddflags & C_FILL)
350                                memset(in.dbp, fill_char, in.dbsz);
351                        else if (ddflags & (C_BLOCK | C_UNBLOCK))
352                                memset(in.dbp, ' ', in.dbsz);
353                        else
354                                memset(in.dbp, 0, in.dbsz);
355                }
356
357                n = read(in.fd, in.dbp, in.dbsz);
358                if (n == 0) {
359                        in.dbrcnt = 0;
360                        return;
361                }
362
363                /* Read error. */
364                if (n == -1) {
365                        /*
366                         * If noerror not specified, die.  POSIX requires that
367                         * the warning message be followed by an I/O display.
368                         */
369                        if (!(ddflags & C_NOERROR))
370                                err(exit_jump, 1, "%s", in.name);
371                        warn("%s", in.name);
372                        summary(globals);
373
374                        /*
375                         * If it's a seekable file descriptor, seek past the
376                         * error.  If your OS doesn't do the right thing for
377                         * raw disks this section should be modified to re-read
378                         * in sector size chunks.
379                         */
380                        if (in.flags & ISSEEK &&
381                            lseek(in.fd, (off_t)in.dbsz, SEEK_CUR))
382                                warn("%s", in.name);
383
384                        /* If sync not specified, omit block and continue. */
385                        if (!(ddflags & C_SYNC))
386                                continue;
387
388                        /* Read errors count as full blocks. */
389                        in.dbcnt += in.dbrcnt = in.dbsz;
390                        ++st.in_full;
391
392                /* Handle full input blocks. */
393                } else if ((size_t)n == in.dbsz) {
394                        in.dbcnt += in.dbrcnt = n;
395                        ++st.in_full;
396
397                /* Handle partial input blocks. */
398                } else {
399                        /* If sync, use the entire block. */
400                        if (ddflags & C_SYNC)
401                                in.dbcnt += in.dbrcnt = in.dbsz;
402                        else
403                                in.dbcnt += in.dbrcnt = n;
404                        ++st.in_part;
405                }
406
407                /*
408                 * POSIX states that if bs is set and no other conversions
409                 * than noerror, notrunc or sync are specified, the block
410                 * is output without buffering as it is read.
411                 */
412                if (ddflags & C_BS) {
413                        out.dbcnt = in.dbcnt;
414                        dd_out(globals, 1);
415                        in.dbcnt = 0;
416                        continue;
417                }
418
419                if (ddflags & C_SWAB) {
420                        if ((n = in.dbrcnt) & 1) {
421                                ++st.swab;
422                                --n;
423                        }
424                        swab(in.dbp, in.dbp, (size_t)n);
425                }
426
427                in.dbp += in.dbrcnt;
428                (*cfunc)(globals);
429        }
430}
431
432/*
433 * Clean up any remaining I/O and flush output.  If necessary, the output file
434 * is truncated.
435 */
436static void
437dd_close(rtems_shell_dd_globals* globals)
438{
439        if (cfunc == def)
440                def_close(globals);
441        else if (cfunc == block)
442                block_close(globals);
443        else if (cfunc == unblock)
444                unblock_close(globals);
445        if (ddflags & C_OSYNC && out.dbcnt && out.dbcnt < out.dbsz) {
446                if (ddflags & C_FILL)
447                        memset(out.dbp, fill_char, out.dbsz - out.dbcnt);
448                else if (ddflags & (C_BLOCK | C_UNBLOCK))
449                        memset(out.dbp, ' ', out.dbsz - out.dbcnt);
450                else
451                        memset(out.dbp, 0, out.dbsz - out.dbcnt);
452                out.dbcnt = out.dbsz;
453        }
454        if (out.dbcnt || pending)
455                dd_out(globals, 1);
456}
457
458void
459dd_out(rtems_shell_dd_globals* globals, int force)
460{
461        u_char *outp;
462        size_t cnt, i, n;
463        ssize_t nw;
464        static int warned;
465        int sparse;
466
467        /*
468         * Write one or more blocks out.  The common case is writing a full
469         * output block in a single write; increment the full block stats.
470         * Otherwise, we're into partial block writes.  If a partial write,
471         * and it's a character device, just warn.  If a tape device, quit.
472         *
473         * The partial writes represent two cases.  1: Where the input block
474         * was less than expected so the output block was less than expected.
475         * 2: Where the input block was the right size but we were forced to
476         * write the block in multiple chunks.  The original versions of dd(1)
477         * never wrote a block in more than a single write, so the latter case
478         * never happened.
479         *
480         * One special case is if we're forced to do the write -- in that case
481         * we play games with the buffer size, and it's usually a partial write.
482         */
483        outp = out.db;
484        for (n = force ? out.dbcnt : out.dbsz;; n = out.dbsz) {
485                for (cnt = n;; cnt -= nw) {
486                        sparse = 0;
487                        if (ddflags & C_SPARSE) {
488                                sparse = 1;     /* Is buffer sparse? */
489                                for (i = 0; i < cnt; i++)
490                                        if (outp[i] != 0) {
491                                                sparse = 0;
492                                                break;
493                                        }
494                        }
495                        if (sparse && !force) {
496                                pending += cnt;
497                                nw = cnt;
498                        } else {
499                                if (pending != 0) {
500                                        if (force)
501                                                pending--;
502                                        if (lseek(out.fd, pending, SEEK_CUR) ==
503                                            -1)
504                                                err(exit_jump, 2, "%s: seek error creating sparse file",
505                                                    out.name);
506                                        if (force)
507                                                write(out.fd, outp, 1);
508                                        pending = 0;
509                                }
510                                if (cnt)
511                                        nw = write(out.fd, outp, cnt);
512                                else
513                                        return;
514                        }
515
516                        if (nw <= 0) {
517                                if (nw == 0)
518                                        errx(exit_jump, 1, "%s: end of device", out.name);
519                                if (errno != EINTR)
520                                        err(exit_jump, 1, "%s", out.name);
521                                nw = 0;
522                        }
523                        outp += nw;
524                        st.bytes += nw;
525                        if ((size_t)nw == n) {
526                                if (n != out.dbsz)
527                                        ++st.out_part;
528                                else
529                                        ++st.out_full;
530                                break;
531                        }
532                        ++st.out_part;
533                        if ((size_t)nw == cnt)
534                                break;
535                        if (out.flags & ISTAPE)
536                                errx(exit_jump, 1, "%s: short write on tape device",
537                                    out.name);
538                        if (out.flags & ISCHR && !warned) {
539                                warned = 1;
540                                warnx("%s: short write on character device",
541                                    out.name);
542                        }
543                }
544                if ((out.dbcnt -= n) < out.dbsz)
545                        break;
546        }
547
548        /* Reassemble the output block. */
549        if (out.dbcnt)
550                (void)memmove(out.db, out.dbp - out.dbcnt, out.dbcnt);
551        out.dbp = out.db + out.dbcnt;
552}
553
554rtems_shell_cmd_t rtems_shell_DD_Command = {
555  "dd",                                                /* name */
556  "dd [OPERAND]...",                                   /* usage */
557  "files",                                             /* topic */
558  rtems_shell_main_dd,                                 /* command */
559  NULL,                                                /* alias */
560  NULL                                                 /* next */
561};
Note: See TracBrowser for help on using the repository browser.