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

Last change on this file since 71923138 was 71923138, checked in by Sebastian Huber <sebastian.huber@…>, on Sep 28, 2017 at 11:18:52 AM

shell: Fix missing prototype warning

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