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