1 /* dd.c - convert/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
8 USE_DD(NEWTOY(dd, 0, TOYFLAG_USR|TOYFLAG_BIN))
9
10 config DD
11 bool "dd"
12 default n
13 help
14 usage: dd [if|of=FILE] [ibs|obs|bs|count|seek|skip=N] [conv|status|iflag|oflag=FLAG[,FLAG...]]
15
16 Copy/convert blocks of data from input to output, with the following
17 keyword=value modifiers (and their default values):
18
19 if=FILE Read FILE (stdin) of=FILE Write to FILE (stdout)
20 bs=N Block size in bytes (512) count=N Stop after copying N blocks
21 ibs=N Input block size (bs=) obs=N Output block size (bs=)
22 skip=N Skip N input blocks (0) seek=N Skip N output blocks (0)
23
24 Each =N value accepts the normal unit suffixes (see toybox --help).
25
26 These modifiers take a comma separated list of potential options:
27
28 iflag=count_bytes,skip_bytes count=N or skip=N is in bytes not blocks
29 oflag=seek_bytes,append seek=N is in bytes, append output to file
30 status=noxfer,none don't show transfer rate, no summary info
31 conv=
32 notrunc Don't truncate output noerror Continue after read errors
33 sync Zero pad short reads fsync Flush output to disk at end
34 sparse Seek past zeroed output excl Fail if output file exists
35 nocreat Fail if of=FILE missing
36 */
37
38 #define FOR_dd
39 #include "toys.h"
40
41 GLOBALS(
42 int show_xfer, show_records;
43 unsigned long long bytes, in_full, in_part, out_full, out_part, start;
44 struct {
45 char *name;
46 int fd;
47 unsigned char *buff, *bp;
48 long sz, count;
49 unsigned long long offset;
50 } in, out;
51 unsigned conv, iflag, oflag;
52 )
53
54 struct dd_flag {
55 char *name;
56 };
57
58 static const struct dd_flag dd_conv[] = TAGGED_ARRAY(DD_conv,
59 {"fsync"}, {"noerror"}, {"notrunc"}, {"sync"}, // TODO sparse excl nocreat
60 );
61
62 static const struct dd_flag dd_iflag[] = TAGGED_ARRAY(DD_iflag,
63 {"count_bytes"}, {"skip_bytes"},
64 );
65
66 static const struct dd_flag dd_oflag[] = TAGGED_ARRAY(DD_oflag,
67 {"seek_bytes"},
68 );
69
status()70 static void status()
71 {
72 unsigned long long now = millitime()-TT.start ? : 1, bytes = TT.bytes*1000;
73
74 if (TT.show_records)
75 fprintf(stderr, "%llu+%llu records in\n%llu+%llu records out\n",
76 TT.in_full, TT.in_part, TT.out_full, TT.out_part);
77
78 if (TT.show_xfer) {
79 human_readable(toybuf, TT.bytes, HR_SPACE|HR_B);
80 fprintf(stderr, "%llu bytes (%s) copied, ", TT.bytes, toybuf);
81 bytes = (bytes>TT.bytes) ? bytes/now : TT.bytes/((now+999)/1000);
82 human_readable(toybuf, bytes, HR_SPACE|HR_B);
83 fprintf(stderr, "%llu.%03u s, %s/s\n", now/1000, (int)(now%1000), toybuf);
84 }
85 }
86
write_out(int all)87 static void write_out(int all)
88 {
89 TT.out.bp = TT.out.buff;
90 while (TT.out.count) {
91 ssize_t nw = writeall(TT.out.fd, TT.out.bp, ((all)? TT.out.count : TT.out.sz));
92
93 all = 0; //further writes will be on obs
94 if (nw <= 0) perror_exit("%s: write error", TT.out.name);
95 if (nw == TT.out.sz) TT.out_full++;
96 else TT.out_part++;
97 TT.out.count -= nw;
98 TT.out.bp += nw;
99 TT.bytes += nw;
100 if (TT.out.count < TT.out.sz) break;
101 }
102 if (TT.out.count) memmove(TT.out.buff, TT.out.bp, TT.out.count); //move remainder to front
103 }
104
parse_flags(char * what,char * arg,const struct dd_flag * flags,int flag_count,unsigned * result)105 static void parse_flags(char *what, char *arg,
106 const struct dd_flag* flags, int flag_count, unsigned *result)
107 {
108 char *pre = xstrdup(arg);
109 int i;
110
111 for (i = 0; i<flag_count; ++i)
112 while (comma_remove(pre, flags[i].name)) *result |= 1<<i;
113 if (*pre) error_exit("bad %s=%s", what, pre);
114 free(pre);
115 }
116
dd_main()117 void dd_main()
118 {
119 char **args, *arg;
120 unsigned long long bs = 0, count = ULLONG_MAX;
121 int trunc = O_TRUNC;
122
123 TT.show_xfer = TT.show_records = 1;
124
125 TT.in.sz = TT.out.sz = 512; //default io block size
126 for (args = toys.optargs; (arg = *args); args++) {
127 if (strstart(&arg, "bs=")) bs = atolx_range(arg, 1, LONG_MAX);
128 else if (strstart(&arg, "ibs=")) TT.in.sz = atolx_range(arg, 1, LONG_MAX);
129 else if (strstart(&arg, "obs=")) TT.out.sz = atolx_range(arg, 1, LONG_MAX);
130 else if (strstart(&arg, "count=")) count = atolx_range(arg, 0, LLONG_MAX);
131 else if (strstart(&arg, "if=")) TT.in.name = arg;
132 else if (strstart(&arg, "of=")) TT.out.name = arg;
133 else if (strstart(&arg, "seek="))
134 TT.out.offset = atolx_range(arg, 0, LLONG_MAX);
135 else if (strstart(&arg, "skip="))
136 TT.in.offset = atolx_range(arg, 0, LLONG_MAX);
137 else if (strstart(&arg, "status=")) {
138 if (!strcmp(arg, "noxfer")) TT.show_xfer = 0;
139 else if (!strcmp(arg, "none")) TT.show_xfer = TT.show_records = 0;
140 else error_exit("unknown status '%s'", arg);
141 } else if (strstart(&arg, "conv="))
142 parse_flags("conv", arg, dd_conv, ARRAY_LEN(dd_conv), &TT.conv);
143 else if (strstart(&arg, "iflag="))
144 parse_flags("iflag", arg, dd_iflag, ARRAY_LEN(dd_iflag), &TT.iflag);
145 else if (strstart(&arg, "oflag="))
146 parse_flags("oflag", arg, dd_oflag, ARRAY_LEN(dd_oflag), &TT.oflag);
147 else error_exit("bad arg %s", arg);
148 }
149 if (bs) TT.in.sz = TT.out.sz = bs;
150
151 sigatexit(status);
152 xsignal(SIGUSR1, status);
153 TT.start = millitime();
154
155 // For bs=, in/out is done as it is. so only in.sz is enough.
156 // With Single buffer there will be overflow in a read following partial read.
157 TT.in.buff = TT.out.buff = xmalloc(TT.in.sz + (bs ? 0 : TT.out.sz));
158 TT.in.bp = TT.out.bp = TT.in.buff;
159
160 if (!TT.in.name) TT.in.name = "stdin";
161 else TT.in.fd = xopenro(TT.in.name);
162
163 if (TT.conv & _DD_conv_notrunc) trunc = 0;
164
165 if (!TT.out.name) {
166 TT.out.name = "stdout";
167 TT.out.fd = 1;
168 } else TT.out.fd = xcreate(TT.out.name,
169 O_WRONLY|O_CREAT|(trunc*!TT.out.offset), 0666);
170
171 // Implement skip=
172 if (TT.in.offset) {
173 off_t off = TT.in.offset;
174
175 if (!(TT.iflag & _DD_iflag_skip_bytes)) off *= TT.in.sz;
176 if (lseek(TT.in.fd, off, SEEK_CUR) < 0) {
177 while (off > 0) {
178 int chunk = off < TT.in.sz ? off : TT.in.sz;
179 ssize_t n = read(TT.in.fd, TT.in.bp, chunk);
180
181 if (n < 0) {
182 perror_msg("%s", TT.in.name);
183 if (TT.conv & _DD_conv_noerror) status();
184 else return;
185 } else if (!n) {
186 xprintf("%s: Can't skip\n", TT.in.name);
187 return;
188 }
189 off -= n;
190 }
191 }
192 }
193
194 // Implement seek= and truncate as necessary. We handled position zero
195 // truncate with O_TRUNC on open, so output to /dev/null etc doesn't error.
196 bs = TT.out.offset;
197 if (!(TT.oflag & _DD_oflag_seek_bytes)) bs *= TT.out.sz;
198 if (bs) {
199 struct stat st;
200
201 xlseek(TT.out.fd, bs, SEEK_CUR);
202 if (trunc && !fstat(TT.out.fd, &st) && S_ISREG(st.st_mode)
203 && ftruncate(TT.out.fd, bs)) perror_exit("truncate");
204 }
205
206 if (!(TT.iflag & _DD_iflag_count_bytes) && count*TT.in.sz>count)
207 count *= TT.in.sz;
208
209 while (count) {
210 int chunk = minof(count, TT.in.sz);
211 ssize_t n;
212
213 TT.in.bp = TT.in.buff + TT.in.count;
214 if (TT.conv & _DD_conv_sync) memset(TT.in.bp, 0, TT.in.sz);
215 errno = 0;
216 if (!(n = read(TT.in.fd, TT.in.bp, chunk))) break;
217 if (n < 0) {
218 if (errno == EINTR) continue;
219 //read error case.
220 perror_msg("%s: read error", TT.in.name);
221 if (!(TT.conv & _DD_conv_noerror)) exit(1);
222 status();
223 xlseek(TT.in.fd, TT.in.sz, SEEK_CUR);
224 if (!(TT.conv & _DD_conv_sync)) continue;
225 // if SYNC, then treat as full block of nuls
226 n = TT.in.sz;
227 }
228 if (n == TT.in.sz) {
229 TT.in_full++;
230 TT.in.count += n;
231 } else {
232 TT.in_part++;
233 if (TT.conv & _DD_conv_sync) TT.in.count += TT.in.sz;
234 else TT.in.count += n;
235 }
236 count -= n;
237
238 TT.out.count = TT.in.count;
239 if (bs) {
240 write_out(1);
241 TT.in.count = 0;
242 continue;
243 }
244
245 if (TT.in.count >= TT.out.sz) {
246 write_out(0);
247 TT.in.count = TT.out.count;
248 }
249 }
250 if (TT.out.count) write_out(1); //write any remaining input blocks
251 if ((TT.conv & _DD_conv_fsync) && fsync(TT.out.fd)<0)
252 perror_exit("%s: fsync", TT.out.name);
253
254 close(TT.in.fd);
255 close(TT.out.fd);
256 if (TT.in.buff) free(TT.in.buff);
257 }
258