• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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