source: branches/3.2/mindi-busybox/coreutils/dd.c @ 3232

Last change on this file since 3232 was 3232, checked in by bruno, 5 years ago
  • Update mindi-busybox to 1.21.1
File size: 10.7 KB
Line 
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini dd implementation for busybox
4 *
5 *
6 * Copyright (C) 2000,2001  Matt Kraai
7 *
8 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
9 */
10
11//usage:#define dd_trivial_usage
12//usage:       "[if=FILE] [of=FILE] " IF_FEATURE_DD_IBS_OBS("[ibs=N] [obs=N] ") "[bs=N] [count=N] [skip=N]\n"
13//usage:       "    [seek=N]" IF_FEATURE_DD_IBS_OBS(" [conv=notrunc|noerror|sync|fsync]")
14//usage:#define dd_full_usage "\n\n"
15//usage:       "Copy a file with converting and formatting\n"
16//usage:     "\n    if=FILE     Read from FILE instead of stdin"
17//usage:     "\n    of=FILE     Write to FILE instead of stdout"
18//usage:     "\n    bs=N        Read and write N bytes at a time"
19//usage:    IF_FEATURE_DD_IBS_OBS(
20//usage:     "\n    ibs=N       Read N bytes at a time"
21//usage:    )
22//usage:    IF_FEATURE_DD_IBS_OBS(
23//usage:     "\n    obs=N       Write N bytes at a time"
24//usage:    )
25//usage:     "\n    count=N     Copy only N input blocks"
26//usage:     "\n    skip=N      Skip N input blocks"
27//usage:     "\n    seek=N      Skip N output blocks"
28//usage:    IF_FEATURE_DD_IBS_OBS(
29//usage:     "\n    conv=notrunc    Don't truncate output file"
30//usage:     "\n    conv=noerror    Continue after read errors"
31//usage:     "\n    conv=sync   Pad blocks with zeros"
32//usage:     "\n    conv=fsync  Physically write data out before finishing"
33//usage:    )
34//usage:     "\n"
35//usage:     "\nNumbers may be suffixed by c (x1), w (x2), b (x512), kD (x1000), k (x1024),"
36//usage:     "\nMD (x1000000), M (x1048576), GD (x1000000000) or G (x1073741824)"
37//usage:
38//usage:#define dd_example_usage
39//usage:       "$ dd if=/dev/zero of=/dev/ram1 bs=1M count=4\n"
40//usage:       "4+0 records in\n"
41//usage:       "4+0 records out\n"
42
43#include "libbb.h"
44
45/* This is a NOEXEC applet. Be very careful! */
46
47
48enum {
49    ifd = STDIN_FILENO,
50    ofd = STDOUT_FILENO,
51};
52
53static const struct suffix_mult dd_suffixes[] = {
54    { "c", 1 },
55    { "w", 2 },
56    { "b", 512 },
57    { "kD", 1000 },
58    { "k", 1024 },
59    { "K", 1024 },  /* compat with coreutils dd */
60    { "MD", 1000000 },
61    { "M", 1048576 },
62    { "GD", 1000000000 },
63    { "G", 1073741824 },
64    { "", 0 }
65};
66
67struct globals {
68    off_t out_full, out_part, in_full, in_part;
69#if ENABLE_FEATURE_DD_THIRD_STATUS_LINE
70    unsigned long long total_bytes;
71    unsigned long long begin_time_us;
72#endif
73} FIX_ALIASING;
74#define G (*(struct globals*)&bb_common_bufsiz1)
75#define INIT_G() do { \
76    /* we have to zero it out because of NOEXEC */ \
77    memset(&G, 0, sizeof(G)); \
78} while (0)
79
80
81static void dd_output_status(int UNUSED_PARAM cur_signal)
82{
83#if ENABLE_FEATURE_DD_THIRD_STATUS_LINE
84    double seconds;
85    unsigned long long bytes_sec;
86    unsigned long long now_us = monotonic_us(); /* before fprintf */
87#endif
88
89    /* Deliberately using %u, not %d */
90    fprintf(stderr, "%"OFF_FMT"u+%"OFF_FMT"u records in\n"
91            "%"OFF_FMT"u+%"OFF_FMT"u records out\n",
92            G.in_full, G.in_part,
93            G.out_full, G.out_part);
94
95#if ENABLE_FEATURE_DD_THIRD_STATUS_LINE
96    fprintf(stderr, "%llu bytes (%sB) copied, ",
97            G.total_bytes,
98            /* show fractional digit, use suffixes */
99            make_human_readable_str(G.total_bytes, 1, 0)
100    );
101    /* Corner cases:
102     * ./busybox dd </dev/null >/dev/null
103     * ./busybox dd bs=1M count=2000 </dev/zero >/dev/null
104     * (echo DONE) | ./busybox dd >/dev/null
105     * (sleep 1; echo DONE) | ./busybox dd >/dev/null
106     */
107    seconds = (now_us - G.begin_time_us) / 1000000.0;
108    bytes_sec = G.total_bytes / seconds;
109    fprintf(stderr, "%f seconds, %sB/s\n",
110            seconds,
111            /* show fractional digit, use suffixes */
112            make_human_readable_str(bytes_sec, 1, 0)
113    );
114#endif
115}
116
117static ssize_t full_write_or_warn(const void *buf, size_t len,
118    const char *const filename)
119{
120    ssize_t n = full_write(ofd, buf, len);
121    if (n < 0)
122        bb_perror_msg("writing '%s'", filename);
123    return n;
124}
125
126static bool write_and_stats(const void *buf, size_t len, size_t obs,
127    const char *filename)
128{
129    ssize_t n = full_write_or_warn(buf, len, filename);
130    if (n < 0)
131        return 1;
132    if ((size_t)n == obs)
133        G.out_full++;
134    else if (n) /* > 0 */
135        G.out_part++;
136#if ENABLE_FEATURE_DD_THIRD_STATUS_LINE
137    G.total_bytes += n;
138#endif
139    return 0;
140}
141
142#if ENABLE_LFS
143# define XATOU_SFX xatoull_sfx
144#else
145# define XATOU_SFX xatoul_sfx
146#endif
147
148int dd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
149int dd_main(int argc UNUSED_PARAM, char **argv)
150{
151    enum {
152        /* Must be in the same order as OP_conv_XXX! */
153        /* (see "flags |= (1 << what)" below) */
154        FLAG_NOTRUNC = 1 << 0,
155        FLAG_SYNC    = 1 << 1,
156        FLAG_NOERROR = 1 << 2,
157        FLAG_FSYNC   = 1 << 3,
158        /* end of conv flags */
159        FLAG_TWOBUFS = 1 << 4,
160        FLAG_COUNT   = 1 << 5,
161    };
162    static const char keywords[] ALIGN1 =
163        "bs\0""count\0""seek\0""skip\0""if\0""of\0"
164#if ENABLE_FEATURE_DD_IBS_OBS
165        "ibs\0""obs\0""conv\0"
166#endif
167        ;
168#if ENABLE_FEATURE_DD_IBS_OBS
169    static const char conv_words[] ALIGN1 =
170        "notrunc\0""sync\0""noerror\0""fsync\0";
171#endif
172    enum {
173        OP_bs = 0,
174        OP_count,
175        OP_seek,
176        OP_skip,
177        OP_if,
178        OP_of,
179#if ENABLE_FEATURE_DD_IBS_OBS
180        OP_ibs,
181        OP_obs,
182        OP_conv,
183        /* Must be in the same order as FLAG_XXX! */
184        OP_conv_notrunc = 0,
185        OP_conv_sync,
186        OP_conv_noerror,
187        OP_conv_fsync,
188    /* Unimplemented conv=XXX: */
189    //nocreat       do not create the output file
190    //excl          fail if the output file already exists
191    //fdatasync     physically write output file data before finishing
192    //swab          swap every pair of input bytes
193    //lcase         change upper case to lower case
194    //ucase         change lower case to upper case
195    //block         pad newline-terminated records with spaces to cbs-size
196    //unblock       replace trailing spaces in cbs-size records with newline
197    //ascii         from EBCDIC to ASCII
198    //ebcdic        from ASCII to EBCDIC
199    //ibm           from ASCII to alternate EBCDIC
200#endif
201    };
202    int exitcode = EXIT_FAILURE;
203    size_t ibs = 512, obs = 512;
204    ssize_t n, w;
205    char *ibuf, *obuf;
206    /* And these are all zeroed at once! */
207    struct {
208        int flags;
209        size_t oc;
210        off_t count;
211        off_t seek, skip;
212        const char *infile, *outfile;
213    } Z;
214#define flags   (Z.flags  )
215#define oc      (Z.oc     )
216#define count   (Z.count  )
217#define seek    (Z.seek   )
218#define skip    (Z.skip   )
219#define infile  (Z.infile )
220#define outfile (Z.outfile)
221
222    memset(&Z, 0, sizeof(Z));
223    INIT_G();
224    //fflush_all(); - is this needed because of NOEXEC?
225
226    for (n = 1; argv[n]; n++) {
227        int what;
228        char *val;
229        char *arg = argv[n];
230
231#if ENABLE_DESKTOP
232        /* "dd --". NB: coreutils 6.9 will complain if they see
233         * more than one of them. We wouldn't. */
234        if (arg[0] == '-' && arg[1] == '-' && arg[2] == '\0')
235            continue;
236#endif
237        val = strchr(arg, '=');
238        if (val == NULL)
239            bb_show_usage();
240        *val = '\0';
241        what = index_in_strings(keywords, arg);
242        if (what < 0)
243            bb_show_usage();
244        /* *val = '='; - to preserve ps listing? */
245        val++;
246#if ENABLE_FEATURE_DD_IBS_OBS
247        if (what == OP_ibs) {
248            /* Must fit into positive ssize_t */
249            ibs = xatoul_range_sfx(val, 1, ((size_t)-1L)/2, dd_suffixes);
250            /*continue;*/
251        }
252        if (what == OP_obs) {
253            obs = xatoul_range_sfx(val, 1, ((size_t)-1L)/2, dd_suffixes);
254            /*continue;*/
255        }
256        if (what == OP_conv) {
257            while (1) {
258                /* find ',', replace them with NUL so we can use val for
259                 * index_in_strings() without copying.
260                 * We rely on val being non-null, else strchr would fault.
261                 */
262                arg = strchr(val, ',');
263                if (arg)
264                    *arg = '\0';
265                what = index_in_strings(conv_words, val);
266                if (what < 0)
267                    bb_error_msg_and_die(bb_msg_invalid_arg, val, "conv");
268                flags |= (1 << what);
269                if (!arg) /* no ',' left, so this was the last specifier */
270                    break;
271                /* *arg = ','; - to preserve ps listing? */
272                val = arg + 1; /* skip this keyword and ',' */
273            }
274            continue; /* we trashed 'what', can't fall through */
275        }
276#endif
277        if (what == OP_bs) {
278            ibs = obs = xatoul_range_sfx(val, 1, ((size_t)-1L)/2, dd_suffixes);
279            /*continue;*/
280        }
281        /* These can be large: */
282        if (what == OP_count) {
283            flags |= FLAG_COUNT;
284            count = XATOU_SFX(val, dd_suffixes);
285            /*continue;*/
286        }
287        if (what == OP_seek) {
288            seek = XATOU_SFX(val, dd_suffixes);
289            /*continue;*/
290        }
291        if (what == OP_skip) {
292            skip = XATOU_SFX(val, dd_suffixes);
293            /*continue;*/
294        }
295        if (what == OP_if) {
296            infile = val;
297            /*continue;*/
298        }
299        if (what == OP_of) {
300            outfile = val;
301            /*continue;*/
302        }
303    } /* end of "for (argv[n])" */
304
305//XXX:FIXME for huge ibs or obs, malloc'ing them isn't the brightest idea ever
306    ibuf = obuf = xmalloc(ibs);
307    if (ibs != obs) {
308        flags |= FLAG_TWOBUFS;
309        obuf = xmalloc(obs);
310    }
311
312#if ENABLE_FEATURE_DD_SIGNAL_HANDLING
313    signal_SA_RESTART_empty_mask(SIGUSR1, dd_output_status);
314#endif
315#if ENABLE_FEATURE_DD_THIRD_STATUS_LINE
316    G.begin_time_us = monotonic_us();
317#endif
318
319    if (infile != NULL)
320        xmove_fd(xopen(infile, O_RDONLY), ifd);
321    else {
322        infile = bb_msg_standard_input;
323    }
324    if (outfile != NULL) {
325        int oflag = O_WRONLY | O_CREAT;
326
327        if (!seek && !(flags & FLAG_NOTRUNC))
328            oflag |= O_TRUNC;
329
330        xmove_fd(xopen(outfile, oflag), ofd);
331
332        if (seek && !(flags & FLAG_NOTRUNC)) {
333            if (ftruncate(ofd, seek * obs) < 0) {
334                struct stat st;
335
336                if (fstat(ofd, &st) < 0
337                 || S_ISREG(st.st_mode)
338                 || S_ISDIR(st.st_mode)
339                ) {
340                    goto die_outfile;
341                }
342            }
343        }
344    } else {
345        outfile = bb_msg_standard_output;
346    }
347    if (skip) {
348        if (lseek(ifd, skip * ibs, SEEK_CUR) < 0) {
349            while (skip-- > 0) {
350                n = safe_read(ifd, ibuf, ibs);
351                if (n < 0)
352                    goto die_infile;
353                if (n == 0)
354                    break;
355            }
356        }
357    }
358    if (seek) {
359        if (lseek(ofd, seek * obs, SEEK_CUR) < 0)
360            goto die_outfile;
361    }
362
363    while (!(flags & FLAG_COUNT) || (G.in_full + G.in_part != count)) {
364        n = safe_read(ifd, ibuf, ibs);
365        if (n == 0)
366            break;
367        if (n < 0) {
368            /* "Bad block" */
369            if (!(flags & FLAG_NOERROR))
370                goto die_infile;
371            bb_simple_perror_msg(infile);
372            /* GNU dd with conv=noerror skips over bad blocks */
373            xlseek(ifd, ibs, SEEK_CUR);
374            /* conv=noerror,sync writes NULs,
375             * conv=noerror just ignores input bad blocks */
376            n = 0;
377        }
378        if ((size_t)n == ibs)
379            G.in_full++;
380        else {
381            G.in_part++;
382            if (flags & FLAG_SYNC) {
383                memset(ibuf + n, 0, ibs - n);
384                n = ibs;
385            }
386        }
387        if (flags & FLAG_TWOBUFS) {
388            char *tmp = ibuf;
389            while (n) {
390                size_t d = obs - oc;
391
392                if (d > (size_t)n)
393                    d = n;
394                memcpy(obuf + oc, tmp, d);
395                n -= d;
396                tmp += d;
397                oc += d;
398                if (oc == obs) {
399                    if (write_and_stats(obuf, obs, obs, outfile))
400                        goto out_status;
401                    oc = 0;
402                }
403            }
404        } else if (write_and_stats(ibuf, n, obs, outfile))
405            goto out_status;
406
407        if (flags & FLAG_FSYNC) {
408            if (fsync(ofd) < 0)
409                goto die_outfile;
410        }
411    }
412
413    if (ENABLE_FEATURE_DD_IBS_OBS && oc) {
414        w = full_write_or_warn(obuf, oc, outfile);
415        if (w < 0) goto out_status;
416        if (w > 0) G.out_part++;
417    }
418    if (close(ifd) < 0) {
419 die_infile:
420        bb_simple_perror_msg_and_die(infile);
421    }
422
423    if (close(ofd) < 0) {
424 die_outfile:
425        bb_simple_perror_msg_and_die(outfile);
426    }
427
428    exitcode = EXIT_SUCCESS;
429 out_status:
430    dd_output_status(0);
431
432    if (ENABLE_FEATURE_CLEAN_UP) {
433        free(obuf);
434        if (flags & FLAG_TWOBUFS)
435            free(ibuf);
436    }
437
438    return exitcode;
439}
Note: See TracBrowser for help on using the repository browser.