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

5
Last change on this file since f97536d was f97536d, checked in by Sebastian Huber <sebastian.huber@…>, on 10/16/15 at 06:21:48

basdefs.h: Add and use RTEMS_UNUSED

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