1 /* dd.c - program to convert and 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 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 
strsuftoll(char * arg,int def,unsigned long long max)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 
summary()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 
sig_handler(int sig)142 static void sig_handler(int sig)
143 {
144   TT.sig = sig;
145 }
146 
xmove_fd(int fd)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 
setup_inout()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 
write_out(int all)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 
do_dd(void)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 
comp(const void * a,const void * b)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 
dd_main()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