10f66f451Sopenharmony_ci/* dd.c - program to convert and copy a file. 20f66f451Sopenharmony_ci * 30f66f451Sopenharmony_ci * Copyright 2013 Ashwini Kumar <ak.ashwini@gmail.com> 40f66f451Sopenharmony_ci * Copyright 2013 Kyungwan Han <asura321@gmail.com> 50f66f451Sopenharmony_ci * 60f66f451Sopenharmony_ci * See http://opengroup.org/onlinepubs/9699919799/utilities/dd.html 70f66f451Sopenharmony_ci 80f66f451Sopenharmony_ciUSE_DD(NEWTOY(dd, 0, TOYFLAG_USR|TOYFLAG_BIN)) 90f66f451Sopenharmony_ci 100f66f451Sopenharmony_ciconfig DD 110f66f451Sopenharmony_ci bool "dd" 120f66f451Sopenharmony_ci default n 130f66f451Sopenharmony_ci help 140f66f451Sopenharmony_ci usage: dd [if=FILE] [of=FILE] [ibs=N] [obs=N] [iflag=FLAGS] [oflag=FLAGS] 150f66f451Sopenharmony_ci [bs=N] [count=N] [seek=N] [skip=N] 160f66f451Sopenharmony_ci [conv=notrunc|noerror|sync|fsync] [status=noxfer|none] 170f66f451Sopenharmony_ci 180f66f451Sopenharmony_ci Copy/convert files. 190f66f451Sopenharmony_ci 200f66f451Sopenharmony_ci if=FILE Read from FILE instead of stdin 210f66f451Sopenharmony_ci of=FILE Write to FILE instead of stdout 220f66f451Sopenharmony_ci bs=N Read and write N bytes at a time 230f66f451Sopenharmony_ci ibs=N Input block size 240f66f451Sopenharmony_ci obs=N Output block size 250f66f451Sopenharmony_ci count=N Copy only N input blocks 260f66f451Sopenharmony_ci skip=N Skip N input blocks 270f66f451Sopenharmony_ci seek=N Skip N output blocks 280f66f451Sopenharmony_ci iflag=FLAGS Set input flags 290f66f451Sopenharmony_ci oflag=FLAGS Set output flags 300f66f451Sopenharmony_ci conv=notrunc Don't truncate output file 310f66f451Sopenharmony_ci conv=noerror Continue after read errors 320f66f451Sopenharmony_ci conv=sync Pad blocks with zeros 330f66f451Sopenharmony_ci conv=fsync Physically write data out before finishing 340f66f451Sopenharmony_ci status=noxfer Don't show transfer rate 350f66f451Sopenharmony_ci status=none Don't show transfer rate or records in/out 360f66f451Sopenharmony_ci 370f66f451Sopenharmony_ci FLAGS is a comma-separated list of: 380f66f451Sopenharmony_ci 390f66f451Sopenharmony_ci count_bytes (iflag) interpret count=N in bytes, not blocks 400f66f451Sopenharmony_ci seek_bytes (oflag) interpret seek=N in bytes, not blocks 410f66f451Sopenharmony_ci skip_bytes (iflag) interpret skip=N in bytes, not blocks 420f66f451Sopenharmony_ci 430f66f451Sopenharmony_ci Numbers may be suffixed by c (*1), w (*2), b (*512), kD (*1000), k (*1024), 440f66f451Sopenharmony_ci MD (*1000*1000), M (*1024*1024), GD (*1000*1000*1000) or G (*1024*1024*1024). 450f66f451Sopenharmony_ci*/ 460f66f451Sopenharmony_ci 470f66f451Sopenharmony_ci#define FOR_dd 480f66f451Sopenharmony_ci#include "toys.h" 490f66f451Sopenharmony_ci#define BS_MAX_SIZE INT32_MAX 500f66f451Sopenharmony_ci 510f66f451Sopenharmony_ciGLOBALS( 520f66f451Sopenharmony_ci int show_xfer, show_records; 530f66f451Sopenharmony_ci unsigned long long bytes, c_count, in_full, in_part, out_full, out_part; 540f66f451Sopenharmony_ci struct timeval start; 550f66f451Sopenharmony_ci struct { 560f66f451Sopenharmony_ci char *name; 570f66f451Sopenharmony_ci int fd; 580f66f451Sopenharmony_ci unsigned char *buff, *bp; 590f66f451Sopenharmony_ci long sz, count; 600f66f451Sopenharmony_ci unsigned long long offset; 610f66f451Sopenharmony_ci } in, out; 620f66f451Sopenharmony_ci unsigned conv, iflag, oflag; 630f66f451Sopenharmony_ci); 640f66f451Sopenharmony_ci 650f66f451Sopenharmony_cistruct dd_flag { 660f66f451Sopenharmony_ci char *name; 670f66f451Sopenharmony_ci}; 680f66f451Sopenharmony_ci 690f66f451Sopenharmony_cistatic const struct dd_flag dd_conv[] = TAGGED_ARRAY(DD_conv, 700f66f451Sopenharmony_ci {"fsync"}, {"noerror"}, {"notrunc"}, {"sync"}, 710f66f451Sopenharmony_ci); 720f66f451Sopenharmony_ci 730f66f451Sopenharmony_cistatic const struct dd_flag dd_iflag[] = TAGGED_ARRAY(DD_iflag, 740f66f451Sopenharmony_ci {"count_bytes"}, {"skip_bytes"}, 750f66f451Sopenharmony_ci); 760f66f451Sopenharmony_ci 770f66f451Sopenharmony_cistatic const struct dd_flag dd_oflag[] = TAGGED_ARRAY(DD_oflag, 780f66f451Sopenharmony_ci {"seek_bytes"}, 790f66f451Sopenharmony_ci); 800f66f451Sopenharmony_ci 810f66f451Sopenharmony_cistatic void status() 820f66f451Sopenharmony_ci{ 830f66f451Sopenharmony_ci double seconds; 840f66f451Sopenharmony_ci struct timeval now; 850f66f451Sopenharmony_ci 860f66f451Sopenharmony_ci gettimeofday(&now, NULL); 870f66f451Sopenharmony_ci seconds = ((now.tv_sec * 1000000 + now.tv_usec) - 880f66f451Sopenharmony_ci (TT.start.tv_sec * 1000000 + TT.start.tv_usec))/1000000.0; 890f66f451Sopenharmony_ci 900f66f451Sopenharmony_ci if (TT.show_records) 910f66f451Sopenharmony_ci fprintf(stderr, "%llu+%llu records in\n%llu+%llu records out\n", 920f66f451Sopenharmony_ci TT.in_full, TT.in_part, TT.out_full, TT.out_part); 930f66f451Sopenharmony_ci 940f66f451Sopenharmony_ci if (TT.show_xfer) { 950f66f451Sopenharmony_ci human_readable(toybuf, TT.bytes, HR_SPACE|HR_B); 960f66f451Sopenharmony_ci fprintf(stderr, "%llu bytes (%s) copied, ", TT.bytes, toybuf); 970f66f451Sopenharmony_ci human_readable(toybuf, TT.bytes/seconds, HR_SPACE|HR_B); 980f66f451Sopenharmony_ci fprintf(stderr, "%f s, %s/s\n", seconds, toybuf); 990f66f451Sopenharmony_ci } 1000f66f451Sopenharmony_ci} 1010f66f451Sopenharmony_ci 1020f66f451Sopenharmony_cistatic void dd_sigint(int sig) { 1030f66f451Sopenharmony_ci status(); 1040f66f451Sopenharmony_ci toys.exitval = sig|128; 1050f66f451Sopenharmony_ci xexit(); 1060f66f451Sopenharmony_ci} 1070f66f451Sopenharmony_ci 1080f66f451Sopenharmony_cistatic void write_out(int all) 1090f66f451Sopenharmony_ci{ 1100f66f451Sopenharmony_ci TT.out.bp = TT.out.buff; 1110f66f451Sopenharmony_ci while (TT.out.count) { 1120f66f451Sopenharmony_ci ssize_t nw = writeall(TT.out.fd, TT.out.bp, ((all)? TT.out.count : TT.out.sz)); 1130f66f451Sopenharmony_ci 1140f66f451Sopenharmony_ci all = 0; //further writes will be on obs 1150f66f451Sopenharmony_ci if (nw <= 0) perror_exit("%s: write error", TT.out.name); 1160f66f451Sopenharmony_ci if (nw == TT.out.sz) TT.out_full++; 1170f66f451Sopenharmony_ci else TT.out_part++; 1180f66f451Sopenharmony_ci TT.out.count -= nw; 1190f66f451Sopenharmony_ci TT.out.bp += nw; 1200f66f451Sopenharmony_ci TT.bytes += nw; 1210f66f451Sopenharmony_ci if (TT.out.count < TT.out.sz) break; 1220f66f451Sopenharmony_ci } 1230f66f451Sopenharmony_ci if (TT.out.count) memmove(TT.out.buff, TT.out.bp, TT.out.count); //move remainder to front 1240f66f451Sopenharmony_ci} 1250f66f451Sopenharmony_ci 1260f66f451Sopenharmony_cistatic void parse_flags(char *what, char *arg, 1270f66f451Sopenharmony_ci const struct dd_flag* flags, int flag_count, unsigned *result) 1280f66f451Sopenharmony_ci{ 1290f66f451Sopenharmony_ci char *pre = xstrdup(arg); 1300f66f451Sopenharmony_ci int i; 1310f66f451Sopenharmony_ci 1320f66f451Sopenharmony_ci for (i=0; i<flag_count; ++i) { 1330f66f451Sopenharmony_ci while (comma_remove(pre, flags[i].name)) *result |= 1<<i; 1340f66f451Sopenharmony_ci } 1350f66f451Sopenharmony_ci if (*pre) error_exit("bad %s=%s", what, pre); 1360f66f451Sopenharmony_ci free(pre); 1370f66f451Sopenharmony_ci} 1380f66f451Sopenharmony_ci 1390f66f451Sopenharmony_civoid dd_main() 1400f66f451Sopenharmony_ci{ 1410f66f451Sopenharmony_ci char **args; 1420f66f451Sopenharmony_ci unsigned long long bs = 0; 1430f66f451Sopenharmony_ci int trunc = O_TRUNC; 1440f66f451Sopenharmony_ci 1450f66f451Sopenharmony_ci TT.show_xfer = TT.show_records = 1; 1460f66f451Sopenharmony_ci TT.c_count = ULLONG_MAX; 1470f66f451Sopenharmony_ci 1480f66f451Sopenharmony_ci TT.in.sz = TT.out.sz = 512; //default io block size 1490f66f451Sopenharmony_ci for (args = toys.optargs; *args; args++) { 1500f66f451Sopenharmony_ci char *arg = *args; 1510f66f451Sopenharmony_ci 1520f66f451Sopenharmony_ci // set BS_MAX_SIZE to 1073741824(1G) to avoid malloc too much space later. 1530f66f451Sopenharmony_ci if (strstart(&arg, "bs=")) bs = atolx_range(arg, 1, BS_MAX_SIZE); 1540f66f451Sopenharmony_ci else if (strstart(&arg, "ibs=")) TT.in.sz = atolx_range(arg, 1, BS_MAX_SIZE); 1550f66f451Sopenharmony_ci else if (strstart(&arg, "obs=")) TT.out.sz = atolx_range(arg, 1, BS_MAX_SIZE); 1560f66f451Sopenharmony_ci else if (strstart(&arg, "count=")) 1570f66f451Sopenharmony_ci TT.c_count = atolx_range(arg, 0, LLONG_MAX); 1580f66f451Sopenharmony_ci else if (strstart(&arg, "if=")) TT.in.name = arg; 1590f66f451Sopenharmony_ci else if (strstart(&arg, "of=")) TT.out.name = arg; 1600f66f451Sopenharmony_ci else if (strstart(&arg, "seek=")) 1610f66f451Sopenharmony_ci TT.out.offset = atolx_range(arg, 0, LLONG_MAX); 1620f66f451Sopenharmony_ci else if (strstart(&arg, "skip=")) 1630f66f451Sopenharmony_ci TT.in.offset = atolx_range(arg, 0, LLONG_MAX); 1640f66f451Sopenharmony_ci else if (strstart(&arg, "status=")) { 1650f66f451Sopenharmony_ci if (!strcmp(arg, "noxfer")) TT.show_xfer = 0; 1660f66f451Sopenharmony_ci else if (!strcmp(arg, "none")) TT.show_xfer = TT.show_records = 0; 1670f66f451Sopenharmony_ci else error_exit("unknown status '%s'", arg); 1680f66f451Sopenharmony_ci } else if (strstart(&arg, "conv=")) { 1690f66f451Sopenharmony_ci parse_flags("conv", arg, dd_conv, ARRAY_LEN(dd_conv), &TT.conv); 1700f66f451Sopenharmony_ci fprintf(stderr, "conv=%x\n", TT.conv); 1710f66f451Sopenharmony_ci } else if (strstart(&arg, "iflag=")) 1720f66f451Sopenharmony_ci parse_flags("iflag", arg, dd_iflag, ARRAY_LEN(dd_iflag), &TT.iflag); 1730f66f451Sopenharmony_ci else if (strstart(&arg, "oflag=")) 1740f66f451Sopenharmony_ci parse_flags("oflag", arg, dd_oflag, ARRAY_LEN(dd_oflag), &TT.oflag); 1750f66f451Sopenharmony_ci else error_exit("bad arg %s", arg); 1760f66f451Sopenharmony_ci } 1770f66f451Sopenharmony_ci if (bs) TT.in.sz = TT.out.sz = bs; 1780f66f451Sopenharmony_ci 1790f66f451Sopenharmony_ci signal(SIGINT, dd_sigint); 1800f66f451Sopenharmony_ci signal(SIGUSR1, generic_signal); 1810f66f451Sopenharmony_ci gettimeofday(&TT.start, NULL); 1820f66f451Sopenharmony_ci 1830f66f451Sopenharmony_ci // For bs=, in/out is done as it is. so only in.sz is enough. 1840f66f451Sopenharmony_ci // With Single buffer there will be overflow in a read following partial read. 1850f66f451Sopenharmony_ci TT.in.buff = TT.out.buff = xmalloc(TT.in.sz + (bs ? 0 : TT.out.sz)); 1860f66f451Sopenharmony_ci TT.in.bp = TT.out.bp = TT.in.buff; 1870f66f451Sopenharmony_ci 1880f66f451Sopenharmony_ci if (!TT.in.name) TT.in.name = "stdin"; 1890f66f451Sopenharmony_ci else TT.in.fd = xopenro(TT.in.name); 1900f66f451Sopenharmony_ci 1910f66f451Sopenharmony_ci if (TT.conv & _DD_conv_notrunc) trunc = 0; 1920f66f451Sopenharmony_ci 1930f66f451Sopenharmony_ci if (!TT.out.name) { 1940f66f451Sopenharmony_ci TT.out.name = "stdout"; 1950f66f451Sopenharmony_ci TT.out.fd = 1; 1960f66f451Sopenharmony_ci } else TT.out.fd = xcreate(TT.out.name, 1970f66f451Sopenharmony_ci O_WRONLY|O_CREAT|(trunc*!TT.out.offset), 0666); 1980f66f451Sopenharmony_ci 1990f66f451Sopenharmony_ci // Implement skip= 2000f66f451Sopenharmony_ci if (TT.in.offset) { 2010f66f451Sopenharmony_ci off_t off = TT.in.offset; 2020f66f451Sopenharmony_ci 2030f66f451Sopenharmony_ci if (!(TT.iflag & _DD_iflag_skip_bytes)) off *= TT.in.sz; 2040f66f451Sopenharmony_ci if (lseek(TT.in.fd, off, SEEK_CUR) < 0) { 2050f66f451Sopenharmony_ci while (off > 0) { 2060f66f451Sopenharmony_ci int chunk = off < TT.in.sz ? off : TT.in.sz; 2070f66f451Sopenharmony_ci ssize_t n = read(TT.in.fd, TT.in.bp, chunk); 2080f66f451Sopenharmony_ci 2090f66f451Sopenharmony_ci if (n < 0) { 2100f66f451Sopenharmony_ci perror_msg("%s", TT.in.name); 2110f66f451Sopenharmony_ci if (TT.conv & _DD_conv_noerror) status(); 2120f66f451Sopenharmony_ci else return; 2130f66f451Sopenharmony_ci } else if (!n) { 2140f66f451Sopenharmony_ci xprintf("%s: Can't skip\n", TT.in.name); 2150f66f451Sopenharmony_ci return; 2160f66f451Sopenharmony_ci } 2170f66f451Sopenharmony_ci off -= n; 2180f66f451Sopenharmony_ci } 2190f66f451Sopenharmony_ci } 2200f66f451Sopenharmony_ci } 2210f66f451Sopenharmony_ci 2220f66f451Sopenharmony_ci // Implement seek= and truncate as necessary. We handled position zero 2230f66f451Sopenharmony_ci // truncate with O_TRUNC on open, so output to /dev/null and such doesn't 2240f66f451Sopenharmony_ci // error. 2250f66f451Sopenharmony_ci bs = TT.out.offset; 2260f66f451Sopenharmony_ci if (!(TT.oflag & _DD_oflag_seek_bytes)) bs *= TT.out.sz; 2270f66f451Sopenharmony_ci if (bs) { 2280f66f451Sopenharmony_ci xlseek(TT.out.fd, bs, SEEK_CUR); 2290f66f451Sopenharmony_ci if (trunc && ftruncate(TT.out.fd, bs)) perror_exit("ftruncate"); 2300f66f451Sopenharmony_ci } 2310f66f451Sopenharmony_ci 2320f66f451Sopenharmony_ci unsigned long long bytes_left = TT.c_count; 2330f66f451Sopenharmony_ci if (TT.c_count != ULLONG_MAX && !(TT.iflag & _DD_iflag_count_bytes)) { 2340f66f451Sopenharmony_ci bytes_left *= TT.in.sz; 2350f66f451Sopenharmony_ci } 2360f66f451Sopenharmony_ci while (bytes_left) { 2370f66f451Sopenharmony_ci int chunk = bytes_left < TT.in.sz ? bytes_left : TT.in.sz; 2380f66f451Sopenharmony_ci ssize_t n; 2390f66f451Sopenharmony_ci 2400f66f451Sopenharmony_ci // Show progress and exit on SIGINT or just continue on SIGUSR1. 2410f66f451Sopenharmony_ci if (toys.signal) { 2420f66f451Sopenharmony_ci status(); 2430f66f451Sopenharmony_ci if (toys.signal==SIGINT) exit_signal(toys.signal); 2440f66f451Sopenharmony_ci toys.signal = 0; 2450f66f451Sopenharmony_ci } 2460f66f451Sopenharmony_ci 2470f66f451Sopenharmony_ci TT.in.bp = TT.in.buff + TT.in.count; 2480f66f451Sopenharmony_ci if (TT.conv & _DD_conv_sync) memset(TT.in.bp, 0, TT.in.sz); 2490f66f451Sopenharmony_ci if (!(n = read(TT.in.fd, TT.in.bp, chunk))) break; 2500f66f451Sopenharmony_ci if (n < 0) { 2510f66f451Sopenharmony_ci if (errno == EINTR) continue; 2520f66f451Sopenharmony_ci //read error case. 2530f66f451Sopenharmony_ci perror_msg("%s: read error", TT.in.name); 2540f66f451Sopenharmony_ci if (!(TT.conv & _DD_conv_noerror)) exit(1); 2550f66f451Sopenharmony_ci status(); 2560f66f451Sopenharmony_ci xlseek(TT.in.fd, TT.in.sz, SEEK_CUR); 2570f66f451Sopenharmony_ci if (!(TT.conv & _DD_conv_sync)) continue; 2580f66f451Sopenharmony_ci // if SYNC, then treat as full block of nuls 2590f66f451Sopenharmony_ci n = TT.in.sz; 2600f66f451Sopenharmony_ci } 2610f66f451Sopenharmony_ci if (n == TT.in.sz) { 2620f66f451Sopenharmony_ci TT.in_full++; 2630f66f451Sopenharmony_ci TT.in.count += n; 2640f66f451Sopenharmony_ci } else { 2650f66f451Sopenharmony_ci TT.in_part++; 2660f66f451Sopenharmony_ci if (TT.conv & _DD_conv_sync) TT.in.count += TT.in.sz; 2670f66f451Sopenharmony_ci else TT.in.count += n; 2680f66f451Sopenharmony_ci } 2690f66f451Sopenharmony_ci bytes_left -= n; 2700f66f451Sopenharmony_ci 2710f66f451Sopenharmony_ci TT.out.count = TT.in.count; 2720f66f451Sopenharmony_ci if (bs) { 2730f66f451Sopenharmony_ci write_out(1); 2740f66f451Sopenharmony_ci TT.in.count = 0; 2750f66f451Sopenharmony_ci continue; 2760f66f451Sopenharmony_ci } 2770f66f451Sopenharmony_ci 2780f66f451Sopenharmony_ci if (TT.in.count >= TT.out.sz) { 2790f66f451Sopenharmony_ci write_out(0); 2800f66f451Sopenharmony_ci TT.in.count = TT.out.count; 2810f66f451Sopenharmony_ci } 2820f66f451Sopenharmony_ci } 2830f66f451Sopenharmony_ci if (TT.out.count) write_out(1); //write any remaining input blocks 2840f66f451Sopenharmony_ci if ((TT.conv & _DD_conv_fsync) && fsync(TT.out.fd)<0) 2850f66f451Sopenharmony_ci perror_exit("%s: fsync", TT.out.name); 2860f66f451Sopenharmony_ci 2870f66f451Sopenharmony_ci close(TT.in.fd); 2880f66f451Sopenharmony_ci close(TT.out.fd); 2890f66f451Sopenharmony_ci if (TT.in.buff) free(TT.in.buff); 2900f66f451Sopenharmony_ci 2910f66f451Sopenharmony_ci status(); 2920f66f451Sopenharmony_ci} 293