Home | History | Annotate | Download | only in pending
      1 /* dd.c - program to convert and copy a file.
      2  *
      3  * Copyright 2013 Ashwini Kumar <ak.ashwini (at) gmail.com>
      4  * Copyright 2013 Kyungwan Han <asura321 (at) gmail.com>
      5  *
      6  * See  http://opengroup.org/onlinepubs/9699919799/utilities/dd.html
      7  *
      8  * todo: ctrl-c doesn't work, the read() is restarting.
      9 
     10 USE_DD(NEWTOY(dd, NULL, TOYFLAG_USR|TOYFLAG_BIN))
     11 
     12 config DD
     13   bool "dd"
     14   default n
     15   help
     16     usage: dd [if=FILE] [of=FILE] [ibs=N] [obs=N] [bs=N] [count=N] [skip=N]
     17             [seek=N] [conv=notrunc|noerror|sync|fsync] [status=noxfer|none]
     18 
     19     Options:
     20     if=FILE   Read from FILE instead of stdin
     21     of=FILE   Write to FILE instead of stdout
     22     bs=N      Read and write N bytes at a time
     23     ibs=N     Read N bytes at a time
     24     obs=N     Write N bytes at a time
     25     count=N   Copy only N input blocks
     26     skip=N    Skip N input blocks
     27     seek=N    Skip N output blocks
     28     conv=notrunc  Don't truncate output file
     29     conv=noerror  Continue after read errors
     30     conv=sync     Pad blocks with zeros
     31     conv=fsync    Physically write data out before finishing
     32     status=noxfer Don't show transfer rate
     33     status=none   Don't show transfer rate or records in/out
     34 
     35     Numbers may be suffixed by c (*1), w (*2), b (*512), kD (*1000), k (*1024),
     36     MD (*1000*1000), M (*1024*1024), GD (*1000*1000*1000) or G (*1024*1024*1024).
     37 */
     38 
     39 #define FOR_dd
     40 #include "toys.h"
     41 
     42 GLOBALS(
     43   int show_xfer;
     44   int show_records;
     45   unsigned long long bytes, c_count, in_full, in_part, out_full, out_part;
     46   struct timeval start;
     47   struct {
     48     char *name;
     49     int fd;
     50     unsigned char *buff, *bp;
     51     long sz, count;
     52     unsigned long long offset;
     53   } in, out;
     54 );
     55 
     56 #define C_SYNC    0x0100
     57 #define C_FSYNC   0x0200
     58 #define C_NOERROR 0x0400
     59 #define C_NOTRUNC 0x0800
     60 
     61 struct pair {
     62   char *name;
     63   unsigned val;
     64 };
     65 
     66 static struct pair suffixes[] = {
     67   { "c", 1 }, { "w", 2 }, { "b", 512 },
     68   { "kD", 1000 }, { "k", 1024 }, { "K", 1024 },
     69   { "MD", 1000000 }, { "M", 1048576 },
     70   { "GD", 1000000000 }, { "G", 1073741824 }
     71 };
     72 
     73 static struct pair clist[] = {
     74   { "fsync",    C_FSYNC },
     75   { "noerror",  C_NOERROR },
     76   { "notrunc",  C_NOTRUNC },
     77   { "sync",     C_SYNC },
     78 };
     79 
     80 static unsigned long long strsuftoll(char *arg, int def, unsigned long long max)
     81 {
     82   unsigned long long result;
     83   char *p = arg;
     84   int i, idx = -1;
     85 
     86   while (isspace(*p)) p++;
     87   if (*p == '-') error_exit("invalid number '%s'", arg);
     88 
     89   errno = 0;
     90   result = strtoull(p, &p, 0);
     91   if (errno == ERANGE || result > max || result < def)
     92     perror_exit("invalid number '%s'", arg);
     93   if (*p != '\0') {
     94     for (i = 0; i < ARRAY_LEN(suffixes); i++)
     95       if (!strcmp(p, suffixes[i].name)) idx = i;
     96     if (idx == -1 || (max/suffixes[idx].val < result))
     97       error_exit("invalid number '%s'", arg);
     98     result *= suffixes[idx].val;
     99   }
    100   return result;
    101 }
    102 
    103 static void status()
    104 {
    105   double seconds;
    106   struct timeval now;
    107 
    108   gettimeofday(&now, NULL);
    109   seconds = ((now.tv_sec * 1000000 + now.tv_usec) -
    110       (TT.start.tv_sec * 1000000 + TT.start.tv_usec))/1000000.0;
    111 
    112   if (TT.show_records)
    113     fprintf(stderr, "%llu+%llu records in\n%llu+%llu records out\n",
    114             TT.in_full, TT.in_part, TT.out_full, TT.out_part);
    115 
    116   if (TT.show_xfer) {
    117     human_readable(toybuf, TT.bytes, HR_SPACE|HR_B);
    118     fprintf(stderr, "%llu bytes (%s) copied, ", TT.bytes, toybuf);
    119     human_readable(toybuf, TT.bytes/seconds, HR_SPACE|HR_B);
    120     fprintf(stderr, "%f s, %s/s\n", seconds, toybuf);
    121   }
    122 }
    123 
    124 static void write_out(int all)
    125 {
    126   TT.out.bp = TT.out.buff;
    127   while (TT.out.count) {
    128     ssize_t nw = writeall(TT.out.fd, TT.out.bp, ((all)? TT.out.count : TT.out.sz));
    129 
    130     all = 0; //further writes will be on obs
    131     if (nw <= 0) perror_exit("%s: write error", TT.out.name);
    132     if (nw == TT.out.sz) TT.out_full++;
    133     else TT.out_part++;
    134     TT.out.count -= nw;
    135     TT.out.bp += nw;
    136     TT.bytes += nw;
    137     if (TT.out.count < TT.out.sz) break;
    138   }
    139   if (TT.out.count) memmove(TT.out.buff, TT.out.bp, TT.out.count); //move remainder to front
    140 }
    141 
    142 int strstarteq(char **a, char *b)
    143 {
    144   char *aa = *a;
    145 
    146   if (!strstart(&aa, b)) return 0;
    147   if (*aa != '=') return 0;
    148   *a = ++aa;
    149 
    150   return 1;
    151 }
    152 
    153 static int comp(const void *a, const void *b) //const to shut compiler up
    154 {
    155   return strcmp(((struct pair*)a)->name, ((struct pair*)b)->name);
    156 }
    157 
    158 void dd_main()
    159 {
    160   struct pair *res, key;
    161   char **args;
    162   unsigned long long bs = 0;
    163   int trunc = O_TRUNC;
    164 
    165   TT.show_xfer = TT.show_records = 1;
    166   TT.c_count = ULLONG_MAX;
    167 
    168   TT.in.sz = TT.out.sz = 512; //default io block size
    169   for (args = toys.optargs; *args; args++) {
    170     char *arg = *args;
    171 
    172     if (strstarteq(&arg, "bs")) bs = strsuftoll(arg, 1, LONG_MAX);
    173     else if (strstarteq(&arg, "ibs")) TT.in.sz = strsuftoll(arg, 1, LONG_MAX);
    174     else if (strstarteq(&arg, "obs")) TT.out.sz = strsuftoll(arg, 1, LONG_MAX);
    175     else if (strstarteq(&arg, "count")) TT.c_count = strsuftoll(arg, 0, ULLONG_MAX-1);
    176     else if (strstarteq(&arg, "if")) TT.in.name = arg;
    177     else if (strstarteq(&arg, "of")) TT.out.name = arg;
    178     else if (strstarteq(&arg, "seek"))
    179       TT.out.offset = strsuftoll(arg, 0, ULLONG_MAX);
    180     else if (strstarteq(&arg, "skip"))
    181       TT.in.offset = strsuftoll(arg, 0, ULLONG_MAX);
    182     else if (strstarteq(&arg, "status")) {
    183       if (!strcmp(arg, "noxfer")) TT.show_xfer = 0;
    184       else if (!strcmp(arg, "none")) TT.show_xfer = TT.show_records = 0;
    185       else error_exit("unknown status '%s'", arg);
    186     } else if (strstarteq(&arg, "conv")) {
    187       while (arg) {
    188         key.name = strsep(&arg, ",");
    189         if (!(res = bsearch(&key, clist, ARRAY_LEN(clist),
    190                 sizeof(struct pair), comp)))
    191           error_exit("unknown conversion %s", key.name);
    192 
    193         toys.optflags |= res->val;
    194       }
    195     } else error_exit("bad arg %s", arg);
    196   }
    197   if (bs) TT.in.sz = TT.out.sz = bs;
    198 
    199   signal(SIGINT, generic_signal);
    200   signal(SIGUSR1, generic_signal);
    201   gettimeofday(&TT.start, NULL);
    202 
    203   /* for bs=, in/out is done as it is. so only in.sz is enough.
    204    * With Single buffer there will be overflow in a read following partial read
    205    */
    206   TT.in.buff = TT.out.buff = xmalloc(TT.in.sz + (bs ? 0 : TT.out.sz));
    207   TT.in.bp = TT.out.bp = TT.in.buff;
    208   //setup input
    209   if (!TT.in.name) TT.in.name = "stdin";
    210   else TT.in.fd = xopenro(TT.in.name);
    211 
    212   if (toys.optflags&C_NOTRUNC) trunc = 0;
    213 
    214   //setup output
    215   if (!TT.out.name) {
    216     TT.out.name = "stdout";
    217     TT.out.fd = 1;
    218   } else TT.out.fd = xcreate(TT.out.name,
    219     O_WRONLY|O_CREAT|(trunc*!TT.out.offset), 0666);
    220 
    221   // Implement skip=
    222   if (TT.in.offset) {
    223     if (lseek(TT.in.fd, (off_t)(TT.in.offset * TT.in.sz), SEEK_CUR) < 0) {
    224       while (TT.in.offset--) {
    225         ssize_t n = read(TT.in.fd, TT.in.bp, TT.in.sz);
    226 
    227         if (n < 0) {
    228           perror_msg("%s", TT.in.name);
    229           if (toys.optflags & C_NOERROR) status();
    230           else return;
    231         } else if (!n) {
    232           xprintf("%s: Can't skip\n", TT.in.name);
    233           return;
    234         }
    235       }
    236     }
    237   }
    238 
    239   // seek/truncate as necessary. We handled position zero truncate with
    240   // O_TRUNC on open, so output to /dev/null and such doesn't error.
    241   if (TT.out.fd!=1 && (bs = TT.out.offset*TT.out.sz)) {
    242     xlseek(TT.out.fd, bs, SEEK_CUR);
    243     if (trunc && ftruncate(TT.out.fd, bs)) perror_exit("ftruncate");
    244   }
    245 
    246   while (TT.c_count==ULLONG_MAX || (TT.in_full + TT.in_part) < TT.c_count) {
    247     ssize_t n;
    248 
    249     // Show progress and exit on SIGINT or just continue on SIGUSR1.
    250     if (toys.signal) {
    251       status();
    252       if (toys.signal==SIGINT) exit_signal(toys.signal);
    253       toys.signal = 0;
    254     }
    255 
    256     TT.in.bp = TT.in.buff + TT.in.count;
    257     if (toys.optflags & C_SYNC) memset(TT.in.bp, 0, TT.in.sz);
    258     if (!(n = read(TT.in.fd, TT.in.bp, TT.in.sz))) break;
    259     if (n < 0) {
    260       if (errno == EINTR) continue;
    261       //read error case.
    262       perror_msg("%s: read error", TT.in.name);
    263       if (!(toys.optflags & C_NOERROR)) exit(1);
    264       status();
    265       xlseek(TT.in.fd, TT.in.sz, SEEK_CUR);
    266       if (!(toys.optflags & C_SYNC)) continue;
    267       // if SYNC, then treat as full block of nuls
    268       n = TT.in.sz;
    269     }
    270     if (n == TT.in.sz) {
    271       TT.in_full++;
    272       TT.in.count += n;
    273     } else {
    274       TT.in_part++;
    275       if (toys.optflags & C_SYNC) TT.in.count += TT.in.sz;
    276       else TT.in.count += n;
    277     }
    278 
    279     TT.out.count = TT.in.count;
    280     if (bs) {
    281       write_out(1);
    282       TT.in.count = 0;
    283       continue;
    284     }
    285 
    286     if (TT.in.count >= TT.out.sz) {
    287       write_out(0);
    288       TT.in.count = TT.out.count;
    289     }
    290   }
    291   if (TT.out.count) write_out(1); //write any remaining input blocks
    292   if (toys.optflags & C_FSYNC && fsync(TT.out.fd) < 0)
    293     perror_exit("%s: fsync fail", TT.out.name);
    294 
    295   close(TT.in.fd);
    296   close(TT.out.fd);
    297   if (TT.in.buff) free(TT.in.buff);
    298 
    299   status();
    300 }
    301