toybox/toys/pending/dd.c
<<
>>
Prefs
   1/* dd.c - convert/copy a file
   2 *
   3 * Copyright 2013 Ashwini Kumar <ak.ashwini@gmail.com>
   4 * Copyright 2013 Kyungwan Han <asura321@gmail.com>
   5 *
   6 * See http://opengroup.org/onlinepubs/9699919799/utilities/dd.html
   7
   8USE_DD(NEWTOY(dd, 0, TOYFLAG_USR|TOYFLAG_BIN))
   9
  10config DD
  11  bool "dd"
  12  default n
  13  help
  14    usage: dd [if|of=FILE] [ibs|obs|bs|count|seek|skip=N] [conv|status|iflag|oflag=FLAG[,FLAG...]]
  15
  16    Copy/convert blocks of data from input to output, with the following
  17    keyword=value modifiers (and their default values):
  18
  19    if=FILE  Read FILE (stdin)          of=FILE  Write to FILE (stdout)
  20       bs=N  Block size in bytes (512)  count=N  Stop after copying N blocks
  21      ibs=N  Input block size (bs=)       obs=N  Output block size (bs=)
  22     skip=N  Skip N input blocks (0)     seek=N  Skip N output blocks (0)
  23
  24    Each =N value accepts the normal unit suffixes (see toybox --help).
  25
  26    These modifiers take a comma separated list of potential options:
  27
  28    iflag=count_bytes,skip_bytes   count=N or skip=N is in bytes not blocks
  29    oflag=seek_bytes,append        seek=N is in bytes, append output to file
  30    status=noxfer,none             don't show transfer rate, no summary info
  31    conv=
  32      notrunc  Don't truncate output    noerror  Continue after read errors
  33      sync     Zero pad short reads     fsync    Flush output to disk at end
  34      sparse   Seek past zeroed output  excl     Fail if output file exists
  35      nocreat  Fail if of=FILE missing
  36*/
  37
  38#define FOR_dd
  39#include "toys.h"
  40
  41GLOBALS(
  42  int show_xfer, show_records;
  43  unsigned long long bytes, in_full, in_part, out_full, out_part, start;
  44  struct {
  45    char *name;
  46    int fd;
  47    unsigned char *buff, *bp;
  48    long sz, count;
  49    unsigned long long offset;
  50  } in, out;
  51  unsigned conv, iflag, oflag;
  52)
  53
  54struct dd_flag {
  55  char *name;
  56};
  57
  58static const struct dd_flag dd_conv[] = TAGGED_ARRAY(DD_conv,
  59  {"fsync"}, {"noerror"}, {"notrunc"}, {"sync"}, // TODO sparse excl nocreat
  60);
  61
  62static const struct dd_flag dd_iflag[] = TAGGED_ARRAY(DD_iflag,
  63  {"count_bytes"}, {"skip_bytes"},
  64);
  65
  66static const struct dd_flag dd_oflag[] = TAGGED_ARRAY(DD_oflag,
  67  {"seek_bytes"},
  68);
  69
  70static void status()
  71{
  72  unsigned long long now = millitime()-TT.start ? : 1, bytes = TT.bytes*1000;
  73
  74  if (TT.show_records)
  75    fprintf(stderr, "%llu+%llu records in\n%llu+%llu records out\n",
  76            TT.in_full, TT.in_part, TT.out_full, TT.out_part);
  77
  78  if (TT.show_xfer) {
  79    human_readable(toybuf, TT.bytes, HR_SPACE|HR_B);
  80    fprintf(stderr, "%llu bytes (%s) copied, ", TT.bytes, toybuf);
  81    bytes = (bytes>TT.bytes) ? bytes/now : TT.bytes/((now+999)/1000);
  82    human_readable(toybuf, bytes, HR_SPACE|HR_B);
  83    fprintf(stderr, "%llu.%03u s, %s/s\n", now/1000, (int)(now%1000), toybuf);
  84  }
  85}
  86
  87static void write_out(int all)
  88{
  89  TT.out.bp = TT.out.buff;
  90  while (TT.out.count) {
  91    ssize_t nw = writeall(TT.out.fd, TT.out.bp, ((all)? TT.out.count : TT.out.sz));
  92
  93    all = 0; //further writes will be on obs
  94    if (nw <= 0) perror_exit("%s: write error", TT.out.name);
  95    if (nw == TT.out.sz) TT.out_full++;
  96    else TT.out_part++;
  97    TT.out.count -= nw;
  98    TT.out.bp += nw;
  99    TT.bytes += nw;
 100    if (TT.out.count < TT.out.sz) break;
 101  }
 102  if (TT.out.count) memmove(TT.out.buff, TT.out.bp, TT.out.count); //move remainder to front
 103}
 104
 105static void parse_flags(char *what, char *arg,
 106    const struct dd_flag* flags, int flag_count, unsigned *result)
 107{
 108  char *pre = xstrdup(arg);
 109  int i;
 110
 111  for (i = 0; i<flag_count; ++i)
 112    while (comma_remove(pre, flags[i].name)) *result |= 1<<i;
 113  if (*pre) error_exit("bad %s=%s", what, pre);
 114  free(pre);
 115}
 116
 117void dd_main()
 118{
 119  char **args, *arg;
 120  unsigned long long bs = 0, count = ULLONG_MAX;
 121  int trunc = O_TRUNC;
 122
 123  TT.show_xfer = TT.show_records = 1;
 124
 125  TT.in.sz = TT.out.sz = 512; //default io block size
 126  for (args = toys.optargs; (arg = *args); args++) {
 127    if (strstart(&arg, "bs=")) bs = atolx_range(arg, 1, LONG_MAX);
 128    else if (strstart(&arg, "ibs=")) TT.in.sz = atolx_range(arg, 1, LONG_MAX);
 129    else if (strstart(&arg, "obs=")) TT.out.sz = atolx_range(arg, 1, LONG_MAX);
 130    else if (strstart(&arg, "count=")) count = atolx_range(arg, 0, LLONG_MAX);
 131    else if (strstart(&arg, "if=")) TT.in.name = arg;
 132    else if (strstart(&arg, "of=")) TT.out.name = arg;
 133    else if (strstart(&arg, "seek="))
 134      TT.out.offset = atolx_range(arg, 0, LLONG_MAX);
 135    else if (strstart(&arg, "skip="))
 136      TT.in.offset = atolx_range(arg, 0, LLONG_MAX);
 137    else if (strstart(&arg, "status=")) {
 138      if (!strcmp(arg, "noxfer")) TT.show_xfer = 0;
 139      else if (!strcmp(arg, "none")) TT.show_xfer = TT.show_records = 0;
 140      else error_exit("unknown status '%s'", arg);
 141    } else if (strstart(&arg, "conv="))
 142      parse_flags("conv", arg, dd_conv, ARRAY_LEN(dd_conv), &TT.conv);
 143    else if (strstart(&arg, "iflag="))
 144      parse_flags("iflag", arg, dd_iflag, ARRAY_LEN(dd_iflag), &TT.iflag);
 145    else if (strstart(&arg, "oflag="))
 146      parse_flags("oflag", arg, dd_oflag, ARRAY_LEN(dd_oflag), &TT.oflag);
 147    else error_exit("bad arg %s", arg);
 148  }
 149  if (bs) TT.in.sz = TT.out.sz = bs;
 150
 151  sigatexit(status);
 152  xsignal(SIGUSR1, status);
 153  TT.start = millitime();
 154
 155  // For bs=, in/out is done as it is. so only in.sz is enough.
 156  // With Single buffer there will be overflow in a read following partial read.
 157  TT.in.buff = TT.out.buff = xmalloc(TT.in.sz + (bs ? 0 : TT.out.sz));
 158  TT.in.bp = TT.out.bp = TT.in.buff;
 159
 160  if (!TT.in.name) TT.in.name = "stdin";
 161  else TT.in.fd = xopenro(TT.in.name);
 162
 163  if (TT.conv & _DD_conv_notrunc) trunc = 0;
 164
 165  if (!TT.out.name) {
 166    TT.out.name = "stdout";
 167    TT.out.fd = 1;
 168  } else TT.out.fd = xcreate(TT.out.name,
 169    O_WRONLY|O_CREAT|(trunc*!TT.out.offset), 0666);
 170
 171  // Implement skip=
 172  if (TT.in.offset) {
 173    off_t off = TT.in.offset;
 174
 175    if (!(TT.iflag & _DD_iflag_skip_bytes)) off *= TT.in.sz;
 176    if (lseek(TT.in.fd, off, SEEK_CUR) < 0) {
 177      while (off > 0) {
 178        int chunk = off < TT.in.sz ? off : TT.in.sz;
 179        ssize_t n = read(TT.in.fd, TT.in.bp, chunk);
 180
 181        if (n < 0) {
 182          perror_msg("%s", TT.in.name);
 183          if (TT.conv & _DD_conv_noerror) status();
 184          else return;
 185        } else if (!n) {
 186          xprintf("%s: Can't skip\n", TT.in.name);
 187          return;
 188        }
 189        off -= n;
 190      }
 191    }
 192  }
 193
 194  // Implement seek= and truncate as necessary. We handled position zero
 195  // truncate with O_TRUNC on open, so output to /dev/null etc doesn't error.
 196  bs = TT.out.offset;
 197  if (!(TT.oflag & _DD_oflag_seek_bytes)) bs *= TT.out.sz;
 198  if (bs) {
 199    struct stat st;
 200
 201    xlseek(TT.out.fd, bs, SEEK_CUR);
 202    if (trunc && !fstat(TT.out.fd, &st) && S_ISREG(st.st_mode)
 203      && ftruncate(TT.out.fd, bs)) perror_exit("truncate");
 204  }
 205
 206  if (!(TT.iflag & _DD_iflag_count_bytes) && count*TT.in.sz>count)
 207    count *= TT.in.sz;
 208
 209  while (count) {
 210    int chunk = minof(count, TT.in.sz);
 211    ssize_t n;
 212
 213    TT.in.bp = TT.in.buff + TT.in.count;
 214    if (TT.conv & _DD_conv_sync) memset(TT.in.bp, 0, TT.in.sz);
 215    errno = 0;
 216    if (!(n = read(TT.in.fd, TT.in.bp, chunk))) break;
 217    if (n < 0) {
 218      if (errno == EINTR) continue;
 219      //read error case.
 220      perror_msg("%s: read error", TT.in.name);
 221      if (!(TT.conv & _DD_conv_noerror)) exit(1);
 222      status();
 223      xlseek(TT.in.fd, TT.in.sz, SEEK_CUR);
 224      if (!(TT.conv & _DD_conv_sync)) continue;
 225      // if SYNC, then treat as full block of nuls
 226      n = TT.in.sz;
 227    }
 228    if (n == TT.in.sz) {
 229      TT.in_full++;
 230      TT.in.count += n;
 231    } else {
 232      TT.in_part++;
 233      if (TT.conv & _DD_conv_sync) TT.in.count += TT.in.sz;
 234      else TT.in.count += n;
 235    }
 236    count -= n;
 237
 238    TT.out.count = TT.in.count;
 239    if (bs) {
 240      write_out(1);
 241      TT.in.count = 0;
 242      continue;
 243    }
 244
 245    if (TT.in.count >= TT.out.sz) {
 246      write_out(0);
 247      TT.in.count = TT.out.count;
 248    }
 249  }
 250  if (TT.out.count) write_out(1); //write any remaining input blocks
 251  if ((TT.conv & _DD_conv_fsync) && fsync(TT.out.fd)<0)
 252    perror_exit("%s: fsync", TT.out.name);
 253
 254  close(TT.in.fd);
 255  close(TT.out.fd);
 256  if (TT.in.buff) free(TT.in.buff);
 257}
 258