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 USE_DD(NEWTOY(dd, NULL, TOYFLAG_USR|TOYFLAG_BIN))
      8 
      9 config DD
     10   bool "dd"
     11   default n
     12     help
     13     usage: dd [if=FILE] [of=FILE] [ibs=N] [obs=N] [bs=N] [count=N] [skip=N]
     14             [seek=N] [conv=notrunc|noerror|sync|fsync]
     15 
     16     Options:
     17     if=FILE   Read from FILE instead of stdin
     18     of=FILE   Write to FILE instead of stdout
     19     bs=N      Read and write N bytes at a time
     20     ibs=N     Read N bytes at a time
     21     obs=N     Write N bytes at a time
     22     count=N   Copy only N input blocks
     23     skip=N    Skip N input blocks
     24     seek=N    Skip N output blocks
     25     conv=notrunc  Don't truncate output file
     26     conv=noerror  Continue after read errors
     27     conv=sync     Pad blocks with zeros
     28     conv=fsync    Physically write data out before finishing
     29 
     30     Numbers may be suffixed by c (x1), w (x2), b (x512), kD (x1000), k (x1024),
     31     MD (x1000000), M (x1048576), GD (x1000000000) or G (x1073741824)
     32     Copy a file, converting and formatting according to the operands.
     33 */
     34 #define FOR_dd
     35 #include "toys.h"
     36 
     37 GLOBALS(
     38   int sig;
     39 )
     40 #define C_CONV    0x0000
     41 #define C_BS      0x0001
     42 #define C_COUNT   0x0002
     43 #define C_IBS     0x0004
     44 #define C_OBS     0x0008
     45 #define C_IF      0x0010
     46 #define C_OF      0x0020
     47 #define C_SEEK    0x0040
     48 #define C_SKIP    0x0080
     49 #define C_SYNC    0x0100
     50 #define C_FSYNC   0x0200
     51 #define C_NOERROR 0x0400
     52 #define C_NOTRUNC 0x0800
     53 
     54 struct io {
     55   char *name;
     56   int fd;
     57   unsigned char *buff, *bp;
     58   long sz, count;
     59   unsigned long long offset;
     60 };
     61 
     62 struct iostat {
     63   unsigned long long in_full, in_part, out_full, out_part, bytes;
     64   struct timeval start;
     65 };
     66 
     67 struct pair {
     68   char *name;
     69   unsigned val;
     70 };
     71 
     72 static struct pair suffixes[] = {
     73   { "c", 1 }, { "w", 2 }, { "b", 512 },
     74   { "kD", 1000 }, { "k", 1024 }, { "K", 1024 },
     75   { "MD", 1000000 }, { "M", 1048576 },
     76   { "GD", 1000000000 }, { "G", 1073741824 }
     77 };
     78 
     79 static struct pair clist[] = {
     80   { "fsync",    C_FSYNC },
     81   { "noerror",  C_NOERROR },
     82   { "notrunc",  C_NOTRUNC },
     83   { "sync",     C_SYNC },
     84 };
     85 
     86 static struct pair operands[] = {
     87   // keep the array sorted by name, bsearch() can be used.
     88   { "bs",    C_BS   },
     89   { "conv",  C_CONV },
     90   { "count", C_COUNT},
     91   { "ibs",   C_IBS  },
     92   { "if",    C_IF   },
     93   { "obs",   C_OBS  },
     94   { "of",    C_OF   },
     95   { "seek",  C_SEEK },
     96   { "skip",  C_SKIP },
     97 };
     98 
     99 static struct io in, out;
    100 static struct iostat st;
    101 static unsigned long long c_count;
    102 
    103 static unsigned long long strsuftoll(char* arg, int def, unsigned long long max)
    104 {
    105   unsigned long long result;
    106   char *endp, *ch = arg;
    107   int i, idx = -1;
    108   errno = 0;
    109 
    110   while (isspace(*ch)) ch++;
    111   if (*ch == '-') error_exit("invalid number '%s'",arg);
    112   result = strtoull(arg, &endp, 10);
    113   if (errno == ERANGE || result > max || result < def)
    114     perror_exit("invalid number '%s'",arg);
    115   if (*endp != '\0') {
    116     for (i = 0; i < ARRAY_LEN(suffixes); i++)
    117       if (!strcmp(endp, suffixes[i].name)) idx = i;
    118     if (idx == -1 || (max/suffixes[idx].val < result))
    119       error_exit("invalid number '%s'",arg);
    120     result = result* suffixes[idx].val;
    121   }
    122   return result;
    123 }
    124 
    125 static void summary()
    126 {
    127   double seconds = 5.0;
    128   struct timeval now;
    129 
    130   gettimeofday(&now, NULL);
    131   seconds = ((now.tv_sec * 1000000 + now.tv_usec) - (st.start.tv_sec * 1000000
    132         + st.start.tv_usec))/1000000.0;
    133   //out to STDERR
    134   fprintf(stderr,"%llu+%llu records in\n%llu+%llu records out\n", st.in_full, st.in_part,
    135       st.out_full, st.out_part);
    136   human_readable(toybuf, st.bytes, HR_SPACE|HR_B);
    137   fprintf(stderr, "%llu bytes (%s) copied, ",st.bytes, toybuf);
    138   human_readable(toybuf, st.bytes/seconds, HR_SPACE|HR_B);
    139   fprintf(stderr, "%f s, %s/s\n", seconds, toybuf);
    140 }
    141 
    142 static void sig_handler(int sig)
    143 {
    144   TT.sig = sig;
    145 }
    146 
    147 static int xmove_fd(int fd)
    148 {
    149   int newfd;
    150 
    151   if (fd > STDERR_FILENO) return fd;
    152   if ((newfd = fcntl(fd, F_DUPFD, 3) < 0)) perror_exit("dupfd IO");
    153   close(fd);
    154   return newfd;
    155 }
    156 
    157 static void setup_inout()
    158 {
    159   ssize_t n;
    160 
    161   /* for C_BS, in/out is done as it is. so only in.sz is enough.
    162    * With Single buffer there will be overflow in a read following partial read
    163    */
    164   in.buff = out.buff = xmalloc(in.sz + ((toys.optflags & C_BS)? 0: out.sz));
    165   in.bp = out.bp = in.buff;
    166   atexit(summary);
    167   //setup input
    168   if (!in.name) {
    169     in.name = "stdin";
    170     in.fd = STDIN_FILENO;
    171   } else {
    172     in.fd = xopen(in.name, O_RDONLY);
    173     in.fd = xmove_fd(in.fd);
    174   }
    175   //setup outout
    176   if (!out.name) {
    177     out.name = "stdout";
    178     out.fd = STDOUT_FILENO;
    179   } else {
    180     out.fd = xcreate(out.name, O_WRONLY | O_CREAT, 0666);
    181     out.fd = xmove_fd(out.fd);
    182   }
    183 
    184   if (in.offset) {
    185     if (lseek(in.fd, (off_t)(in.offset * in.sz), SEEK_CUR) < 0) {
    186       while (in.offset--) {
    187         if ((n = read(in.fd, in.bp, in.sz)) < 0) {
    188           if (toys.optflags & C_NOERROR) { //warn message and summary
    189             error_msg("%s: read error", in.name);
    190             summary();
    191           } else perror_exit("%s: read error", in.name);
    192         } else if (!n) {
    193           xprintf("%s: Can't skip\n", in.name);
    194           exit(0);
    195         }
    196       }
    197     }
    198   }
    199 
    200   if (out.offset) xlseek(out.fd, (off_t)(out.offset * out.sz), SEEK_CUR);
    201 }
    202 
    203 static void write_out(int all)
    204 {
    205   ssize_t nw;
    206   out.bp = out.buff;
    207   while (out.count) {
    208     nw = writeall(out.fd, out.bp, ((all)? out.count : out.sz));
    209     all = 0; //further writes will be on obs
    210     if (nw <= 0) perror_exit("%s: write error",out.name);
    211     if (nw == out.sz) st.out_full++;
    212     else st.out_part++;
    213     out.count -= nw;
    214     out.bp += nw;
    215     st.bytes += nw;
    216     if (out.count < out.sz) break;
    217   }
    218   if (out.count) memmove(out.buff, out.bp, out.count); //move remainder to front
    219 }
    220 
    221 static void do_dd(void)
    222 {
    223   ssize_t n;
    224   struct sigaction sa;
    225 
    226   memset(&sa, 0, sizeof(sa));
    227   sa.sa_handler = sig_handler;
    228   sigaction(SIGINT, &sa, NULL);
    229   sigaction(SIGUSR1, &sa, NULL);
    230   setup_inout();
    231   gettimeofday(&st.start, NULL);
    232 
    233   if (toys.optflags & (C_OF | C_SEEK) && !(toys.optflags & C_NOTRUNC))
    234     ftruncate(out.fd, (off_t)out.offset * out.sz);
    235 
    236   while (!(toys.optflags & C_COUNT) || (st.in_full + st.in_part) < c_count) {
    237     if (TT.sig == SIGUSR1) {
    238       summary();
    239       TT.sig = 0;
    240     } else if (TT.sig == SIGINT) exit(TT.sig | 128);
    241     in.bp = in.buff + in.count;
    242     if (toys.optflags & C_SYNC) memset(in.bp, 0, in.sz);
    243     if (!(n = read(in.fd, in.bp, in.sz))) break;
    244     if (n < 0) {
    245       if (errno == EINTR) continue;
    246       //read error case.
    247       perror_msg("%s: read error", in.name);
    248       if (!(toys.optflags & C_NOERROR)) exit(1);
    249       summary();
    250       xlseek(in.fd, in.sz, SEEK_CUR);
    251       if (!(toys.optflags & C_SYNC)) continue;
    252       // if SYNC, then treat as full block of nuls
    253       n = in.sz;
    254     }
    255     if (n == in.sz) {
    256       st.in_full++;
    257       in.count += n;
    258     } else {
    259       st.in_part++;
    260       if (toys.optflags & C_SYNC) in.count += in.sz;
    261       else in.count += n;
    262     }
    263 
    264     out.count = in.count;
    265     if (toys.optflags & C_BS) {
    266       write_out(1);
    267       in.count = 0;
    268       continue;
    269     }
    270 
    271     if (in.count >= out.sz) {
    272       write_out(0);
    273       in.count = out.count;
    274     }
    275   }
    276   if (out.count) write_out(1); //write any remaining input blocks
    277   if (toys.optflags & C_FSYNC && fsync(out.fd) < 0)
    278     perror_exit("%s: fsync fail", out.name);
    279 
    280   close(in.fd);
    281   close(out.fd);
    282   if (in.buff) free(in.buff);
    283 }
    284 
    285 static int comp(const void *a, const void *b) //const to shut compiler up
    286 {
    287   return strcmp(((struct pair*)a)->name, ((struct pair*)b)->name);
    288 }
    289 
    290 void dd_main()
    291 {
    292   struct pair *res, key;
    293   char *arg;
    294   long sz;
    295 
    296   in.sz = out.sz = 512; //default io block size
    297   while (*toys.optargs) {
    298     if (!(arg = strchr(*toys.optargs, '='))) error_exit("unknown arg %s", *toys.optargs);
    299     *arg++ = '\0';
    300     if (!*arg) help_exit(0);
    301     key.name = *toys.optargs;
    302     if (!(res = bsearch(&key, operands, ARRAY_LEN(operands), sizeof(struct pair),
    303             comp))) error_exit("unknown arg %s", key.name);
    304 
    305     toys.optflags |= res->val;
    306     switch(res->val) {
    307       case C_BS:
    308         in.sz = out.sz = strsuftoll(arg, 1, LONG_MAX);
    309         break;
    310       case C_IBS:
    311         sz = strsuftoll(arg, 1, LONG_MAX);
    312         if (!(toys.optflags & C_BS)) in.sz = sz;
    313         break;
    314       case C_OBS:
    315         sz = strsuftoll(arg, 1, LONG_MAX);
    316         if (!(toys.optflags & C_BS)) out.sz = sz;
    317         break;
    318       case C_COUNT:
    319         c_count = strsuftoll(arg, 0, ULLONG_MAX);
    320         break;
    321       case C_IF:
    322         in.name = arg;
    323         break;
    324       case C_OF:
    325         out.name = arg;
    326         break;
    327       case C_SEEK:
    328         out.offset = strsuftoll(arg, 0, ULLONG_MAX);
    329         break;
    330       case C_SKIP:
    331         in.offset = strsuftoll(arg, 0, ULLONG_MAX);
    332         break;
    333       case C_CONV:
    334         while (arg) {
    335           key.name = strsep(&arg, ",");
    336           if (!(res = bsearch(&key, clist, ARRAY_LEN(clist),
    337                   sizeof(struct pair), comp)))
    338             error_exit("unknown conversion %s", key.name);
    339 
    340           toys.optflags |= res->val;
    341         }
    342         break;
    343     }
    344     toys.optargs++;
    345   }
    346 
    347   do_dd();
    348 }
    349