1 /* dd.c - convert/copy a file
2 *
3 * Copyright 2013 Ashwini Kumar <[email protected]>
4 * Copyright 2013 Kyungwan Han <[email protected]>
5 *
6 * See http://opengroup.org/onlinepubs/9699919799/utilities/dd.html
7 *
8 * Deviations from posix: no conversions, no cbs=
9 * TODO: seek=n with unseekable output? (Read output and... write it back?)
10
11 USE_DD(NEWTOY(dd, 0, TOYFLAG_USR|TOYFLAG_BIN))
12
13 config DD
14 bool "dd"
15 default y
16 help
17 usage: dd [if|of=FILE] [ibs|obs|bs|count|seek|skip=N] [conv|status|iflag|oflag=FLAG[,FLAG...]]
18
19 Copy/convert blocks of data from input to output, with the following
20 keyword=value modifiers (and their default values):
21
22 if=FILE Read FILE (stdin) of=FILE Write to FILE (stdout)
23 bs=N Block size in bytes (512) count=N Stop after copying N blocks (all)
24 ibs=N Input block size (bs=) obs=N Output block size (bs=)
25 skip=N Skip N input blocks (0) seek=N Skip N output blocks (0)
26
27 Each =N value accepts the normal unit suffixes (see toybox --help).
28
29 These modifiers take a comma separated list of potential options:
30
31 iflag=count_bytes,skip_bytes count=N or skip=N is in bytes not blocks
32 oflag=seek_bytes,append seek=N is in bytes, append output to file
33 status=noxfer,none don't show transfer rate, no summary info
34 conv=
35 notrunc Don't truncate output noerror Continue after read errors
36 sync Zero pad short reads fsync Flush output to disk at end
37 sparse Seek past zeroed output excl Fail if output file exists
38 nocreat Fail if of=FILE missing
39 */
40
41 #define FOR_dd
42 #include "toys.h"
43
44 GLOBALS(
45 // Display fields
46 int show_xfer, show_records;
47 unsigned long long bytes, in_full, in_part, out_full, out_part, start;
48 )
49
50 struct dd_flag {
51 char *name;
52 };
53
54 static const struct dd_flag dd_conv[] = TAGGED_ARRAY(DD_conv,
55 {"fsync"}, {"noerror"}, {"notrunc"}, {"sync"}, // TODO sparse excl nocreat
56 );
57
58 static const struct dd_flag dd_iflag[] = TAGGED_ARRAY(DD_iflag,
59 {"count_bytes"}, {"skip_bytes"},
60 );
61
62 static const struct dd_flag dd_oflag[] = TAGGED_ARRAY(DD_oflag,
63 {"seek_bytes"},
64 );
65
status()66 static void status()
67 {
68 unsigned long long now = millitime()-TT.start ? : 1, bytes = TT.bytes*1000;
69
70 if (TT.show_records)
71 fprintf(stderr, "%llu+%llu records in\n%llu+%llu records out\n",
72 TT.in_full, TT.in_part, TT.out_full, TT.out_part);
73
74 if (TT.show_xfer) {
75 human_readable(toybuf, TT.bytes, HR_SPACE|HR_B);
76 fprintf(stderr, "%llu bytes (%s) copied, ", TT.bytes, toybuf);
77 bytes = (bytes>TT.bytes) ? bytes/now : TT.bytes/((now+999)/1000);
78 human_readable(toybuf, bytes, HR_SPACE|HR_B);
79 fprintf(stderr, "%llu.%03u s, %s/s\n", now/1000, (int)(now%1000), toybuf);
80 }
81 }
82
parse_flags(char * what,char * arg,const struct dd_flag * flags,int flag_count,unsigned * result)83 static void parse_flags(char *what, char *arg,
84 const struct dd_flag* flags, int flag_count, unsigned *result)
85 {
86 char *pre = xstrdup(arg);
87 int i;
88
89 for (i = 0; i<flag_count; ++i)
90 while (comma_remove(pre, flags[i].name)) *result |= 1<<i;
91 if (*pre) error_exit("bad %s=%s", what, pre);
92 free(pre);
93 }
94
95 // Multiply detecting overflow.
overmul(unsigned long long x,unsigned long long y)96 static unsigned long long overmul(unsigned long long x, unsigned long long y)
97 {
98 unsigned long long ll = x*y;
99
100 if (x && y && (ll<x || ll<y)) error_exit("overflow");
101
102 return ll;
103 }
104
105 // Handle funky posix 1x2x3 syntax.
argxarg(char * arg,int cap)106 static unsigned long long argxarg(char *arg, int cap)
107 {
108 long long ll = 1;
109 char x QUIET, *s, *new;
110
111 arg = xstrdup(arg);
112 for (new = s = arg;; new = s+1) {
113 // atolx() handles 0x hex prefixes, so skip past those looking for separator
114 if ((s = strchr(new+2*!strncmp(new, "0x", 2), 'x'))) {
115 if (s==new) break;
116 x = *s;
117 *s = 0;
118 }
119 ll = overmul(ll, atolx(new));
120 if (s) *s = x;
121 else break;
122 }
123 if (s || ll<cap || ll>(cap ? LONG_MAX : LLONG_MAX)) error_exit("bad %s", arg);
124 free(arg);
125
126 return ll;
127 }
128
129
130 // Point 1 or 2 iovec at len bytes at buf, starting at "start" and wrapping
131 // around at buflen.
iovwrap(char * buf,unsigned long long buflen,unsigned long long start,unsigned long long len,struct iovec * iov)132 int iovwrap(char *buf, unsigned long long buflen, unsigned long long start,
133 unsigned long long len, struct iovec *iov)
134 {
135 iov[0].iov_base = buf + start;
136 iov[0].iov_len = len;
137 if (start+len<=buflen) return 1;
138
139 iov[1].iov_len = len-(iov[0].iov_len = buflen-start);
140 iov[1].iov_base = buf;
141
142 return 2;
143 }
144
dd_main()145 void dd_main()
146 {
147 char **args, *arg, *iname = 0, *oname = 0, *buf;
148 unsigned long long bs = 0, seek = 0, skip = 0, ibs = 512, obs = 512,
149 count = ULLONG_MAX, buflen;
150 long long len;
151 struct iovec iov[2];
152 int opos, olen, ifd = 0, ofd = 1, trunc = O_TRUNC, ii;
153 unsigned conv = 0, iflag = 0, oflag = 0;
154
155 TT.show_xfer = TT.show_records = 1;
156
157 for (args = toys.optargs; (arg = *args); args++) {
158 if (strstart(&arg, "bs=")) bs = argxarg(arg, 1);
159 else if (strstart(&arg, "ibs=")) ibs = argxarg(arg, 1);
160 else if (strstart(&arg, "obs=")) obs = argxarg(arg, 1);
161 else if (strstart(&arg, "count=")) count = argxarg(arg, 0);
162 else if (strstart(&arg, "if=")) iname = arg;
163 else if (strstart(&arg, "of=")) oname = arg;
164 else if (strstart(&arg, "seek=")) seek = argxarg(arg, 0);
165 else if (strstart(&arg, "skip=")) skip = argxarg(arg, 0);
166 else if (strstart(&arg, "status=")) {
167 if (!strcmp(arg, "noxfer")) TT.show_xfer = 0;
168 else if (!strcmp(arg, "none")) TT.show_xfer = TT.show_records = 0;
169 else error_exit("unknown status '%s'", arg);
170 } else if (strstart(&arg, "conv="))
171 parse_flags("conv", arg, dd_conv, ARRAY_LEN(dd_conv), &conv);
172 else if (strstart(&arg, "iflag="))
173 parse_flags("iflag", arg, dd_iflag, ARRAY_LEN(dd_iflag), &iflag);
174 else if (strstart(&arg, "oflag="))
175 parse_flags("oflag", arg, dd_oflag, ARRAY_LEN(dd_oflag), &oflag);
176 else error_exit("bad arg %s", arg);
177 }
178 if (bs) ibs = obs = bs; // bs overrides ibs and obs regardless of position
179
180 TT.start = millitime();
181 sigatexit(status);
182 xsignal(SIGUSR1, status);
183
184 // If bs set, output blocks match input blocks (passing along short reads).
185 // Else read ibs blocks and write obs, which worst case requires ibs+obs-1.
186 buf = xmalloc(buflen = ibs+obs*!bs);
187 if (buflen<ibs || buflen<obs) error_exit("tilt");
188
189 if (conv & _DD_conv_notrunc) trunc = 0;
190 if (iname) ifd = xopenro(iname);
191 else iname = "stdin";
192 if (oname) ofd = xcreate(oname, O_WRONLY|O_CREAT|(trunc*!seek),0666);
193 else oname = "stdout";
194
195 // Implement skip=
196 if (skip) {
197 if (!(iflag & _DD_iflag_skip_bytes)) skip *= ibs;
198 if (lseek(ifd, skip, SEEK_CUR) < 0) {
199 for (; skip > 0; skip -= len) {
200 len = read(ifd, buf, minof(skip, ibs));
201 if (len < 0) {
202 perror_msg_raw(iname);
203 if (conv & _DD_conv_noerror) status();
204 else return;
205 } else if (!len) return xprintf("%s: Can't skip\n", iname);
206 }
207 }
208 }
209
210 // Implement seek= and truncate as necessary. We handled position zero
211 // truncate with O_TRUNC on open, so output to /dev/null etc doesn't error.
212 if (!(oflag & _DD_oflag_seek_bytes)) seek *= obs;
213 if (seek) {
214 struct stat st;
215
216 xlseek(ofd, seek, SEEK_CUR);
217 if (trunc && !fstat(ofd, &st) && S_ISREG(st.st_mode) && ftruncate(ofd,seek))
218 perror_exit("truncate");
219 }
220
221 if (count!=ULLONG_MAX && !(iflag & _DD_iflag_count_bytes))
222 count = overmul(count, ibs);
223
224 // output start position, output bytes available
225 opos = olen = 0;
226 for (;;) {
227 // Write as many output blocks as we can. Using writev() avoids memmove()
228 // to realign data but is still a single atomic write.
229 while (olen>=obs || (olen && (bs || !count))) {
230 errno = 0;
231 len = writev(ofd, iov, iovwrap(buf, buflen, opos, minof(obs, olen), iov));
232 if (len<1) {
233 if (errno==EINTR) continue;
234 perror_exit("%s: write error", oname);
235 }
236 TT.bytes += len;
237 olen -= len;
238 if ((opos += len)>=buflen) opos -= buflen;
239 if (len == obs) TT.out_full++;
240 else TT.out_part++;
241 }
242 if (!count) break;
243
244 // Read next block of input. (There MUST be enough space, we sized buf.)
245 len = opos+olen;
246 if (len>buflen) len -= buflen;
247 errno = 0;
248 if (2 == (ii = iovwrap(buf, buflen, len, minof(count, ibs), iov)))
249 memset(iov[1].iov_base, 0, iov[1].iov_len);
250 memset(iov[0].iov_base, 0, iov[0].iov_len);
251 len = readv(ifd, iov, ii);
252 if (len<1) {
253 if (errno==EINTR) continue;
254 if (!len) count = 0;
255 else {
256 //read error case.
257 perror_msg("%s: read error", iname);
258 if (!(conv & _DD_conv_noerror)) xexit();
259
260 // Complain and try to seek past it
261 status();
262 lseek(ifd, ibs, SEEK_CUR);
263 if (conv & _DD_conv_sync) olen += ibs;
264 }
265
266 continue;
267 }
268 if (len == ibs) TT.in_full++;
269 else TT.in_part++;
270 if (conv & _DD_conv_sync) len = ibs;
271 olen += len;
272 count -= minof(len, count);
273 }
274 if ((conv & _DD_conv_fsync) && fsync(ofd)) perror_exit("%s: fsync", oname);
275
276 if (CFG_TOYBOX_FREE) {
277 close(ifd);
278 close(ofd);
279 free(buf);
280 }
281 }
282