source: rtems/cpukit/libmisc/shell/main_dd.c @ 9a77af8

4.104.115
Last change on this file since 9a77af8 was 9a77af8, checked in by Ralf Corsepius <ralf.corsepius@…>, on 03/26/10 at 17:18:43

Add HAVE_CONFIG_H support to let files receive configure defines.

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