xref: /aosp_15_r20/external/toybox/toys/posix/dd.c (revision cf5a6c84e2b8763fc1a7db14496fd4742913b199)
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