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