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