busybox/coreutils/dd.c
<<
>>
Prefs
   1/* vi: set sw=4 ts=4: */
   2/*
   3 * Mini dd implementation for busybox
   4 *
   5 * Copyright (C) 2000,2001  Matt Kraai
   6 *
   7 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
   8 */
   9//config:config DD
  10//config:       bool "dd (7.1 kb)"
  11//config:       default y
  12//config:       help
  13//config:       dd copies a file (from standard input to standard output,
  14//config:       by default) using specific input and output blocksizes,
  15//config:       while optionally performing conversions on it.
  16//config:
  17//config:config FEATURE_DD_SIGNAL_HANDLING
  18//config:       bool "Enable signal handling for status reporting"
  19//config:       default y
  20//config:       depends on DD
  21//config:       help
  22//config:       Sending a SIGUSR1 signal to a running 'dd' process makes it
  23//config:       print to standard error the number of records read and written
  24//config:       so far, then to resume copying.
  25//config:
  26//config:       $ dd if=/dev/zero of=/dev/null &
  27//config:       $ pid=$!; kill -USR1 $pid; sleep 1; kill $pid
  28//config:       10899206+0 records in
  29//config:       10899206+0 records out
  30//config:
  31//config:config FEATURE_DD_THIRD_STATUS_LINE
  32//config:       bool "Enable the third status line upon signal"
  33//config:       default y
  34//config:       depends on DD && FEATURE_DD_SIGNAL_HANDLING
  35//config:       help
  36//config:       Displays a coreutils-like third status line with transferred bytes,
  37//config:       elapsed time and speed.
  38//config:
  39//config:config FEATURE_DD_IBS_OBS
  40//config:       bool "Enable ibs, obs, iflag and conv options"
  41//config:       default y
  42//config:       depends on DD
  43//config:       help
  44//config:       Enable support for writing a certain number of bytes in and out,
  45//config:       at a time, and performing conversions on the data stream.
  46//config:
  47//config:config FEATURE_DD_STATUS
  48//config:       bool "Enable status display options"
  49//config:       default y
  50//config:       depends on DD
  51//config:       help
  52//config:       Enable support for status=noxfer/none option.
  53
  54//applet:IF_DD(APPLET_NOEXEC(dd, dd, BB_DIR_BIN, BB_SUID_DROP, dd))
  55
  56//kbuild:lib-$(CONFIG_DD) += dd.o
  57
  58//usage:#define dd_trivial_usage
  59//usage:       "[if=FILE] [of=FILE] " IF_FEATURE_DD_IBS_OBS("[ibs=N] [obs=N] ") "[bs=N] [count=N] [skip=N]\n"
  60//usage:       "        [seek=N]" IF_FEATURE_DD_IBS_OBS(" [conv=notrunc|noerror|sync|fsync] [iflag=skip_bytes|fullblock]")
  61//usage:#define dd_full_usage "\n\n"
  62//usage:       "Copy a file with converting and formatting\n"
  63//usage:     "\n        if=FILE         Read from FILE instead of stdin"
  64//usage:     "\n        of=FILE         Write to FILE instead of stdout"
  65//usage:     "\n        bs=N            Read and write N bytes at a time"
  66//usage:        IF_FEATURE_DD_IBS_OBS(
  67//usage:     "\n        ibs=N           Read N bytes at a time"
  68//usage:        )
  69//usage:        IF_FEATURE_DD_IBS_OBS(
  70//usage:     "\n        obs=N           Write N bytes at a time"
  71//usage:        )
  72//usage:     "\n        count=N         Copy only N input blocks"
  73//usage:     "\n        skip=N          Skip N input blocks"
  74//usage:     "\n        seek=N          Skip N output blocks"
  75//usage:        IF_FEATURE_DD_IBS_OBS(
  76//usage:     "\n        conv=notrunc    Don't truncate output file"
  77//usage:     "\n        conv=noerror    Continue after read errors"
  78//usage:     "\n        conv=sync       Pad blocks with zeros"
  79//usage:     "\n        conv=fsync      Physically write data out before finishing"
  80//usage:     "\n        conv=swab       Swap every pair of bytes"
  81//usage:     "\n        iflag=skip_bytes        skip=N is in bytes"
  82//usage:     "\n        iflag=fullblock Read full blocks"
  83//usage:        )
  84//usage:        IF_FEATURE_DD_STATUS(
  85//usage:     "\n        status=noxfer   Suppress rate output"
  86//usage:     "\n        status=none     Suppress all output"
  87//usage:        )
  88//usage:     "\n"
  89//usage:     "\nN may be suffixed by c (1), w (2), b (512), kB (1000), k (1024), MB, M, GB, G"
  90//usage:
  91//usage:#define dd_example_usage
  92//usage:       "$ dd if=/dev/zero of=/dev/ram1 bs=1M count=4\n"
  93//usage:       "4+0 records in\n"
  94//usage:       "4+0 records out\n"
  95
  96#include "libbb.h"
  97#include "common_bufsiz.h"
  98
  99/* This is a NOEXEC applet. Be very careful! */
 100
 101
 102enum {
 103        ifd = STDIN_FILENO,
 104        ofd = STDOUT_FILENO,
 105};
 106
 107struct globals {
 108        off_t out_full, out_part, in_full, in_part;
 109#if ENABLE_FEATURE_DD_THIRD_STATUS_LINE
 110        unsigned long long total_bytes;
 111        unsigned long long begin_time_us;
 112#endif
 113        int flags;
 114} FIX_ALIASING;
 115#define G (*(struct globals*)bb_common_bufsiz1)
 116#define INIT_G() do { \
 117        setup_common_bufsiz(); \
 118        /* we have to zero it out because of NOEXEC */ \
 119        memset(&G, 0, sizeof(G)); \
 120} while (0)
 121
 122enum {
 123        /* Must be in the same order as OP_conv_XXX! */
 124        /* (see "flags |= (1 << what)" below) */
 125        FLAG_NOTRUNC = (1 << 0) * ENABLE_FEATURE_DD_IBS_OBS,
 126        FLAG_SYNC    = (1 << 1) * ENABLE_FEATURE_DD_IBS_OBS,
 127        FLAG_NOERROR = (1 << 2) * ENABLE_FEATURE_DD_IBS_OBS,
 128        FLAG_FSYNC   = (1 << 3) * ENABLE_FEATURE_DD_IBS_OBS,
 129        FLAG_SWAB    = (1 << 4) * ENABLE_FEATURE_DD_IBS_OBS,
 130        /* end of conv flags */
 131        /* start of input flags */
 132        FLAG_IFLAG_SHIFT = 5,
 133        FLAG_SKIP_BYTES = (1 << 5) * ENABLE_FEATURE_DD_IBS_OBS,
 134        FLAG_FULLBLOCK = (1 << 6) * ENABLE_FEATURE_DD_IBS_OBS,
 135        /* end of input flags */
 136        FLAG_TWOBUFS = (1 << 7) * ENABLE_FEATURE_DD_IBS_OBS,
 137        FLAG_COUNT   = 1 << 8,
 138        FLAG_STATUS_NONE = 1 << 9,
 139        FLAG_STATUS_NOXFER = 1 << 10,
 140};
 141
 142static void dd_output_status(int UNUSED_PARAM cur_signal)
 143{
 144#if ENABLE_FEATURE_DD_THIRD_STATUS_LINE
 145        double seconds;
 146        unsigned long long bytes_sec;
 147        unsigned long long now_us = monotonic_us(); /* before fprintf */
 148#endif
 149
 150        /* Deliberately using %u, not %d */
 151        fprintf(stderr, "%"OFF_FMT"u+%"OFF_FMT"u records in\n"
 152                        "%"OFF_FMT"u+%"OFF_FMT"u records out\n",
 153                        G.in_full, G.in_part,
 154                        G.out_full, G.out_part);
 155
 156#if ENABLE_FEATURE_DD_THIRD_STATUS_LINE
 157# if ENABLE_FEATURE_DD_STATUS
 158        if (G.flags & FLAG_STATUS_NOXFER) /* status=noxfer active? */
 159                return;
 160        //TODO: should status=none make dd stop reacting to USR1 entirely?
 161        //So far we react to it (we print the stats),
 162        //status=none only suppresses final, non-USR1 generated status message.
 163# endif
 164        fprintf(stderr, "%llu bytes (%sB) copied, ",
 165                        G.total_bytes,
 166                        /* show fractional digit, use suffixes */
 167                        make_human_readable_str(G.total_bytes, 1, 0)
 168        );
 169        /* Corner cases:
 170         * ./busybox dd </dev/null >/dev/null
 171         * ./busybox dd bs=1M count=2000 </dev/zero >/dev/null
 172         * (echo DONE) | ./busybox dd >/dev/null
 173         * (sleep 1; echo DONE) | ./busybox dd >/dev/null
 174         */
 175        seconds = (now_us - G.begin_time_us) / 1000000.0;
 176        bytes_sec = G.total_bytes / seconds;
 177        fprintf(stderr, "%f seconds, %sB/s\n",
 178                        seconds,
 179                        /* show fractional digit, use suffixes */
 180                        make_human_readable_str(bytes_sec, 1, 0)
 181        );
 182#endif
 183}
 184
 185static ssize_t full_write_or_warn(const void *buf, size_t len,
 186        const char *const filename)
 187{
 188        ssize_t n = full_write(ofd, buf, len);
 189        if (n < 0)
 190                bb_perror_msg("writing '%s'", filename);
 191        return n;
 192}
 193
 194static bool write_and_stats(const void *buf, size_t len, size_t obs,
 195        const char *filename)
 196{
 197        ssize_t n = full_write_or_warn(buf, len, filename);
 198        if (n < 0)
 199                return 1;
 200#if ENABLE_FEATURE_DD_THIRD_STATUS_LINE
 201        G.total_bytes += n;
 202#endif
 203        if ((size_t)n == obs) {
 204                G.out_full++;
 205                return 0;
 206        }
 207        if ((size_t)n == len) {
 208                G.out_part++;
 209                return 0;
 210        }
 211        return 1;
 212}
 213
 214#if ENABLE_LFS
 215# define XATOU_SFX xatoull_sfx
 216#else
 217# define XATOU_SFX xatoul_sfx
 218#endif
 219
 220#if ENABLE_FEATURE_DD_IBS_OBS
 221static int parse_comma_flags(char *val, const char *words, const char *error_in)
 222{
 223        int flags = 0;
 224        while (1) {
 225                int n;
 226                char *arg;
 227                /* find ',', replace them with NUL so we can use val for
 228                 * index_in_strings() without copying.
 229                 * We rely on val being non-null, else strchr would fault.
 230                 */
 231                arg = strchr(val, ',');
 232                if (arg)
 233                        *arg = '\0';
 234                n = index_in_strings(words, val);
 235                if (n < 0)
 236                        bb_error_msg_and_die(bb_msg_invalid_arg_to, val, error_in);
 237                flags |= (1 << n);
 238                if (!arg) /* no ',' left, so this was the last specifier */
 239                        break;
 240                *arg = ','; /* to preserve ps listing */
 241                val = arg + 1; /* skip this keyword and ',' */
 242        }
 243        return flags;
 244}
 245#endif
 246
 247int dd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 248int dd_main(int argc UNUSED_PARAM, char **argv)
 249{
 250        static const char keywords[] ALIGN1 =
 251                "bs\0""count\0""seek\0""skip\0""if\0""of\0"IF_FEATURE_DD_STATUS("status\0")
 252#if ENABLE_FEATURE_DD_IBS_OBS
 253                "ibs\0""obs\0""conv\0""iflag\0"
 254#endif
 255                ;
 256#if ENABLE_FEATURE_DD_IBS_OBS
 257        static const char conv_words[] ALIGN1 =
 258                "notrunc\0""sync\0""noerror\0""fsync\0""swab\0";
 259        static const char iflag_words[] ALIGN1 =
 260                "skip_bytes\0""fullblock\0";
 261#endif
 262#if ENABLE_FEATURE_DD_STATUS
 263        static const char status_words[] ALIGN1 =
 264                "none\0""noxfer\0";
 265#endif
 266        enum {
 267                OP_bs = 0,
 268                OP_count,
 269                OP_seek,
 270                OP_skip,
 271                OP_if,
 272                OP_of,
 273                IF_FEATURE_DD_STATUS(OP_status,)
 274#if ENABLE_FEATURE_DD_IBS_OBS
 275                OP_ibs,
 276                OP_obs,
 277                OP_conv,
 278                OP_iflag,
 279                /* Must be in the same order as FLAG_XXX! */
 280                OP_conv_notrunc = 0,
 281                OP_conv_sync,
 282                OP_conv_noerror,
 283                OP_conv_fsync,
 284                OP_conv_swab,
 285        /* Unimplemented conv=XXX: */
 286        //nocreat       do not create the output file
 287        //excl          fail if the output file already exists
 288        //fdatasync     physically write output file data before finishing
 289        //lcase         change upper case to lower case
 290        //ucase         change lower case to upper case
 291        //block         pad newline-terminated records with spaces to cbs-size
 292        //unblock       replace trailing spaces in cbs-size records with newline
 293        //ascii         from EBCDIC to ASCII
 294        //ebcdic        from ASCII to EBCDIC
 295        //ibm           from ASCII to alternate EBCDIC
 296        /* Partially implemented: */
 297        //swab          swap every pair of input bytes: will abort on non-even reads
 298                OP_iflag_skip_bytes,
 299                OP_iflag_fullblock,
 300#endif
 301        };
 302        smallint exitcode = EXIT_FAILURE;
 303        int i;
 304        size_t ibs = 512;
 305        char *ibuf;
 306#if ENABLE_FEATURE_DD_IBS_OBS
 307        size_t obs = 512;
 308        char *obuf;
 309#else
 310# define obs  ibs
 311# define obuf ibuf
 312#endif
 313        /* These are all zeroed at once! */
 314        struct {
 315                size_t oc;
 316                ssize_t prev_read_size; /* for detecting swab failure */
 317                off_t count;
 318                off_t seek, skip;
 319                const char *infile, *outfile;
 320        } Z;
 321#define oc      (Z.oc     )
 322#define prev_read_size (Z.prev_read_size)
 323#define count   (Z.count  )
 324#define seek    (Z.seek   )
 325#define skip    (Z.skip   )
 326#define infile  (Z.infile )
 327#define outfile (Z.outfile)
 328
 329        memset(&Z, 0, sizeof(Z));
 330        INIT_G();
 331        //fflush_all(); - is this needed because of NOEXEC?
 332
 333        for (i = 1; argv[i]; i++) {
 334                int what;
 335                char *val;
 336                char *arg = argv[i];
 337
 338#if ENABLE_DESKTOP
 339                /* "dd --". NB: coreutils 6.9 will complain if they see
 340                 * more than one of them. We wouldn't. */
 341                if (arg[0] == '-' && arg[1] == '-' && arg[2] == '\0')
 342                        continue;
 343#endif
 344                val = strchr(arg, '=');
 345                if (val == NULL)
 346                        bb_show_usage();
 347                *val = '\0';
 348                what = index_in_strings(keywords, arg);
 349                if (what < 0)
 350                        bb_show_usage();
 351                /* *val = '='; - to preserve ps listing? */
 352                val++;
 353#if ENABLE_FEATURE_DD_IBS_OBS
 354                if (what == OP_ibs) {
 355                        /* Must fit into positive ssize_t */
 356                        ibs = xatoul_range_sfx(val, 1, ((size_t)-1L)/2, cwbkMG_suffixes);
 357                        /*continue;*/
 358                }
 359                if (what == OP_obs) {
 360                        obs = xatoul_range_sfx(val, 1, ((size_t)-1L)/2, cwbkMG_suffixes);
 361                        /*continue;*/
 362                }
 363                if (what == OP_conv) {
 364                        G.flags |= parse_comma_flags(val, conv_words, "conv");
 365                        /*continue;*/
 366                }
 367                if (what == OP_iflag) {
 368                        G.flags |= parse_comma_flags(val, iflag_words, "iflag") << FLAG_IFLAG_SHIFT;
 369                        /*continue;*/
 370                }
 371#endif
 372                if (what == OP_bs) {
 373                        ibs = xatoul_range_sfx(val, 1, ((size_t)-1L)/2, cwbkMG_suffixes);
 374                        obs = ibs;
 375                        /*continue;*/
 376                }
 377                /* These can be large: */
 378                if (what == OP_count) {
 379                        G.flags |= FLAG_COUNT;
 380                        count = XATOU_SFX(val, cwbkMG_suffixes);
 381                        /*continue;*/
 382                }
 383                if (what == OP_seek) {
 384                        seek = XATOU_SFX(val, cwbkMG_suffixes);
 385                        /*continue;*/
 386                }
 387                if (what == OP_skip) {
 388                        skip = XATOU_SFX(val, cwbkMG_suffixes);
 389                        /*continue;*/
 390                }
 391                if (what == OP_if) {
 392                        infile = val;
 393                        /*continue;*/
 394                }
 395                if (what == OP_of) {
 396                        outfile = val;
 397                        /*continue;*/
 398                }
 399#if ENABLE_FEATURE_DD_STATUS
 400                if (what == OP_status) {
 401                        int n;
 402                        n = index_in_strings(status_words, val);
 403                        if (n < 0)
 404                                bb_error_msg_and_die(bb_msg_invalid_arg_to, val, "status");
 405                        G.flags |= FLAG_STATUS_NONE << n;
 406                        /*continue;*/
 407                }
 408#endif
 409        } /* end of "for (argv[i])" */
 410
 411//XXX:FIXME for huge ibs or obs, malloc'ing them isn't the brightest idea ever
 412        ibuf = xmalloc(ibs);
 413        obuf = ibuf;
 414#if ENABLE_FEATURE_DD_IBS_OBS
 415        if (ibs != obs) {
 416                G.flags |= FLAG_TWOBUFS;
 417                obuf = xmalloc(obs);
 418        }
 419#endif
 420
 421#if ENABLE_FEATURE_DD_SIGNAL_HANDLING
 422        signal_SA_RESTART_empty_mask(SIGUSR1, dd_output_status);
 423#endif
 424#if ENABLE_FEATURE_DD_THIRD_STATUS_LINE
 425        G.begin_time_us = monotonic_us();
 426#endif
 427
 428        if (infile) {
 429                xmove_fd(xopen(infile, O_RDONLY), ifd);
 430        } else {
 431                infile = bb_msg_standard_input;
 432        }
 433        if (outfile) {
 434                int oflag = O_WRONLY | O_CREAT;
 435
 436                if (!seek && !(G.flags & FLAG_NOTRUNC))
 437                        oflag |= O_TRUNC;
 438
 439                xmove_fd(xopen(outfile, oflag), ofd);
 440
 441                if (seek && !(G.flags & FLAG_NOTRUNC)) {
 442                        if (ftruncate(ofd, seek * obs) < 0) {
 443                                struct stat st;
 444
 445                                if (fstat(ofd, &st) < 0
 446                                 || S_ISREG(st.st_mode)
 447                                 || S_ISDIR(st.st_mode)
 448                                ) {
 449                                        goto die_outfile;
 450                                }
 451                        }
 452                }
 453        } else {
 454                outfile = bb_msg_standard_output;
 455        }
 456        if (skip) {
 457                size_t blocksz = (G.flags & FLAG_SKIP_BYTES) ? 1 : ibs;
 458                if (lseek(ifd, skip * blocksz, SEEK_CUR) < 0) {
 459                        do {
 460                                ssize_t n;
 461#if ENABLE_FEATURE_DD_IBS_OBS
 462                                if (G.flags & FLAG_FULLBLOCK)
 463                                        n = full_read(ifd, ibuf, blocksz);
 464                                else
 465#endif
 466                                        n = safe_read(ifd, ibuf, blocksz);
 467                                if (n < 0)
 468                                        goto die_infile;
 469                                if (n == 0)
 470                                        break;
 471                        } while (--skip != 0);
 472                }
 473        }
 474        if (seek) {
 475                if (lseek(ofd, seek * obs, SEEK_CUR) < 0)
 476                        goto die_outfile;
 477        }
 478
 479        while (!(G.flags & FLAG_COUNT) || (G.in_full + G.in_part != count)) {
 480                ssize_t n;
 481#if ENABLE_FEATURE_DD_IBS_OBS
 482                if (G.flags & FLAG_FULLBLOCK)
 483                        n = full_read(ifd, ibuf, ibs);
 484                else
 485#endif
 486                        n = safe_read(ifd, ibuf, ibs);
 487                if (n == 0)
 488                        break;
 489                if (n < 0) {
 490                        /* "Bad block" */
 491                        if (!(G.flags & FLAG_NOERROR))
 492                                goto die_infile;
 493                        bb_simple_perror_msg(infile);
 494                        /* GNU dd with conv=noerror skips over bad blocks */
 495                        xlseek(ifd, ibs, SEEK_CUR);
 496                        /* conv=noerror,sync writes NULs,
 497                         * conv=noerror just ignores input bad blocks */
 498                        n = 0;
 499                }
 500                if (G.flags & FLAG_SWAB) {
 501                        uint16_t *p16;
 502                        ssize_t n2;
 503
 504                        /* Our code allows only last read to be odd-sized */
 505                        if (prev_read_size & 1)
 506                                bb_error_msg_and_die("can't swab %lu byte buffer",
 507                                                (unsigned long)prev_read_size);
 508                        prev_read_size = n;
 509
 510                        /* If n is odd, last byte is not swapped:
 511                         *  echo -n "qwe" | dd conv=swab
 512                         * prints "wqe".
 513                         */
 514                        p16 = (void*) ibuf;
 515                        n2 = (n >> 1);
 516                        while (--n2 >= 0) {
 517                                *p16 = bswap_16(*p16);
 518                                p16++;
 519                        }
 520                }
 521                if ((size_t)n == ibs)
 522                        G.in_full++;
 523                else {
 524                        G.in_part++;
 525                        if (G.flags & FLAG_SYNC) {
 526                                memset(ibuf + n, 0, ibs - n);
 527                                n = ibs;
 528                        }
 529                }
 530                if (G.flags & FLAG_TWOBUFS) {
 531                        char *tmp = ibuf;
 532                        while (n) {
 533                                size_t d = obs - oc;
 534
 535                                if (d > (size_t)n)
 536                                        d = n;
 537                                memcpy(obuf + oc, tmp, d);
 538                                n -= d;
 539                                tmp += d;
 540                                oc += d;
 541                                if (oc == obs) {
 542                                        if (write_and_stats(obuf, obs, obs, outfile))
 543                                                goto out_status;
 544                                        oc = 0;
 545                                }
 546                        }
 547                } else {
 548                        if (write_and_stats(ibuf, n, obs, outfile))
 549                                goto out_status;
 550                }
 551        }
 552
 553        if (G.flags & FLAG_FSYNC) {
 554                if (fsync(ofd) < 0)
 555                        goto die_outfile;
 556        }
 557
 558        if (ENABLE_FEATURE_DD_IBS_OBS && oc) {
 559                if (write_and_stats(obuf, oc, obs, outfile))
 560                        goto out_status;
 561        }
 562        if (close(ifd) < 0) {
 563 die_infile:
 564                bb_simple_perror_msg_and_die(infile);
 565        }
 566
 567        if (close(ofd) < 0) {
 568 die_outfile:
 569                bb_simple_perror_msg_and_die(outfile);
 570        }
 571
 572        exitcode = EXIT_SUCCESS;
 573 out_status:
 574        if (!ENABLE_FEATURE_DD_STATUS || !(G.flags & FLAG_STATUS_NONE))
 575                dd_output_status(0);
 576
 577        if (ENABLE_FEATURE_CLEAN_UP) {
 578                free(obuf);
 579                if (G.flags & FLAG_TWOBUFS)
 580                        free(ibuf);
 581        }
 582
 583        return exitcode;
 584}
 585