• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  *
8  * todo: ctrl-c doesn't work, the read() is restarting.
9 
10 USE_DD(NEWTOY(dd, 0, TOYFLAG_USR|TOYFLAG_BIN))
11 
12 config DD
13   bool "dd"
14   default n
15   help
16     usage: dd [if=FILE] [of=FILE] [ibs=N] [obs=N] [bs=N] [count=N] [skip=N]
17             [seek=N] [conv=notrunc|noerror|sync|fsync] [status=noxfer|none]
18 
19     Copy/convert files.
20 
21     if=FILE		Read from FILE instead of stdin
22     of=FILE		Write to FILE instead of stdout
23     bs=N		Read and write N bytes at a time
24     ibs=N		Read N bytes at a time
25     obs=N		Write N bytes at a time
26     count=N		Copy only N input blocks
27     skip=N		Skip N input blocks
28     seek=N		Skip N output blocks
29     conv=notrunc	Don't truncate output file
30     conv=noerror	Continue after read errors
31     conv=sync	Pad blocks with zeros
32     conv=fsync	Physically write data out before finishing
33     status=noxfer	Don't show transfer rate
34     status=none	Don't show transfer rate or records in/out
35 
36     Numbers may be suffixed by c (*1), w (*2), b (*512), kD (*1000), k (*1024),
37     MD (*1000*1000), M (*1024*1024), GD (*1000*1000*1000) or G (*1024*1024*1024).
38 */
39 
40 #define FOR_dd
41 #include "toys.h"
42 
43 GLOBALS(
44   int show_xfer, show_records;
45   unsigned long long bytes, c_count, in_full, in_part, out_full, out_part;
46   struct timeval start;
47   struct {
48     char *name;
49     int fd;
50     unsigned char *buff, *bp;
51     long sz, count;
52     unsigned long long offset;
53   } in, out;
54 );
55 
56 #define C_FSYNC   1
57 #define C_NOERROR 2
58 #define C_NOTRUNC 4
59 #define C_SYNC    8
60 
status()61 static void status()
62 {
63   double seconds;
64   struct timeval now;
65 
66   gettimeofday(&now, NULL);
67   seconds = ((now.tv_sec * 1000000 + now.tv_usec) -
68       (TT.start.tv_sec * 1000000 + TT.start.tv_usec))/1000000.0;
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     human_readable(toybuf, TT.bytes/seconds, HR_SPACE|HR_B);
78     fprintf(stderr, "%f s, %s/s\n", seconds, toybuf);
79   }
80 }
81 
write_out(int all)82 static void write_out(int all)
83 {
84   TT.out.bp = TT.out.buff;
85   while (TT.out.count) {
86     ssize_t nw = writeall(TT.out.fd, TT.out.bp, ((all)? TT.out.count : TT.out.sz));
87 
88     all = 0; //further writes will be on obs
89     if (nw <= 0) perror_exit("%s: write error", TT.out.name);
90     if (nw == TT.out.sz) TT.out_full++;
91     else TT.out_part++;
92     TT.out.count -= nw;
93     TT.out.bp += nw;
94     TT.bytes += nw;
95     if (TT.out.count < TT.out.sz) break;
96   }
97   if (TT.out.count) memmove(TT.out.buff, TT.out.bp, TT.out.count); //move remainder to front
98 }
99 
strstarteq(char ** a,char * b)100 int strstarteq(char **a, char *b)
101 {
102   char *aa = *a;
103 
104   return strstart(&aa, b) && *aa == '=' && (*a = aa+1);
105 }
106 
dd_main()107 void dd_main()
108 {
109   char **args;
110   unsigned long long bs = 0;
111   int trunc = O_TRUNC, conv = 0;
112 
113   TT.show_xfer = TT.show_records = 1;
114   TT.c_count = ULLONG_MAX;
115 
116   TT.in.sz = TT.out.sz = 512; //default io block size
117   for (args = toys.optargs; *args; args++) {
118     char *arg = *args;
119 
120     if (strstarteq(&arg, "bs")) bs = atolx_range(arg, 1, LONG_MAX);
121     else if (strstarteq(&arg, "ibs")) TT.in.sz = atolx_range(arg, 1, LONG_MAX);
122     else if (strstarteq(&arg, "obs")) TT.out.sz = atolx_range(arg, 1, LONG_MAX);
123     else if (strstarteq(&arg, "count"))
124       TT.c_count = atolx_range(arg, 0, LLONG_MAX);
125     else if (strstarteq(&arg, "if")) TT.in.name = arg;
126     else if (strstarteq(&arg, "of")) TT.out.name = arg;
127     else if (strstarteq(&arg, "seek"))
128       TT.out.offset = atolx_range(arg, 0, LLONG_MAX);
129     else if (strstarteq(&arg, "skip"))
130       TT.in.offset = atolx_range(arg, 0, LLONG_MAX);
131     else if (strstarteq(&arg, "status")) {
132       if (!strcmp(arg, "noxfer")) TT.show_xfer = 0;
133       else if (!strcmp(arg, "none")) TT.show_xfer = TT.show_records = 0;
134       else error_exit("unknown status '%s'", arg);
135     } else if (strstarteq(&arg, "conv")) {
136       char *ss, *convs[] = {"fsync", "noerror", "notrunc", "sync"};
137       int i, len;
138 
139       while ((ss = comma_iterate(&arg, &len))) {
140         for (i = 0; i<ARRAY_LEN(convs); i++)
141           if (len == strlen(convs[i]) && !strncmp(ss, convs[i], len)) break;
142         if (i == ARRAY_LEN(convs)) error_exit("bad conv=%.*s", len, ss);
143         conv |= 1<<i;
144       }
145     } else error_exit("bad arg %s", arg);
146   }
147   if (bs) TT.in.sz = TT.out.sz = bs;
148 
149   signal(SIGINT, generic_signal);
150   signal(SIGUSR1, generic_signal);
151   gettimeofday(&TT.start, NULL);
152 
153   /* for bs=, in/out is done as it is. so only in.sz is enough.
154    * With Single buffer there will be overflow in a read following partial read
155    */
156   TT.in.buff = TT.out.buff = xmalloc(TT.in.sz + (bs ? 0 : TT.out.sz));
157   TT.in.bp = TT.out.bp = TT.in.buff;
158   //setup input
159   if (!TT.in.name) TT.in.name = "stdin";
160   else TT.in.fd = xopenro(TT.in.name);
161 
162   if (conv&C_NOTRUNC) trunc = 0;
163 
164   //setup output
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     if (lseek(TT.in.fd, (off_t)(TT.in.offset * TT.in.sz), SEEK_CUR) < 0) {
174       while (TT.in.offset--) {
175         ssize_t n = read(TT.in.fd, TT.in.bp, TT.in.sz);
176 
177         if (n < 0) {
178           perror_msg("%s", TT.in.name);
179           if (conv&C_NOERROR) status();
180           else return;
181         } else if (!n) {
182           xprintf("%s: Can't skip\n", TT.in.name);
183           return;
184         }
185       }
186     }
187   }
188 
189   // seek/truncate as necessary. We handled position zero truncate with
190   // O_TRUNC on open, so output to /dev/null and such doesn't error.
191   if (TT.out.fd!=1 && (bs = TT.out.offset*TT.out.sz)) {
192     xlseek(TT.out.fd, bs, SEEK_CUR);
193     if (trunc && ftruncate(TT.out.fd, bs)) perror_exit("ftruncate");
194   }
195 
196   while (TT.c_count==ULLONG_MAX || (TT.in_full + TT.in_part) < TT.c_count) {
197     ssize_t n;
198 
199     // Show progress and exit on SIGINT or just continue on SIGUSR1.
200     if (toys.signal) {
201       status();
202       if (toys.signal==SIGINT) exit_signal(toys.signal);
203       toys.signal = 0;
204     }
205 
206     TT.in.bp = TT.in.buff + TT.in.count;
207     if (conv&C_SYNC) memset(TT.in.bp, 0, TT.in.sz);
208     if (!(n = read(TT.in.fd, TT.in.bp, TT.in.sz))) break;
209     if (n < 0) {
210       if (errno == EINTR) continue;
211       //read error case.
212       perror_msg("%s: read error", TT.in.name);
213       if (!(conv&C_NOERROR)) exit(1);
214       status();
215       xlseek(TT.in.fd, TT.in.sz, SEEK_CUR);
216       if (!(conv&C_SYNC)) continue;
217       // if SYNC, then treat as full block of nuls
218       n = TT.in.sz;
219     }
220     if (n == TT.in.sz) {
221       TT.in_full++;
222       TT.in.count += n;
223     } else {
224       TT.in_part++;
225       if (conv&C_SYNC) TT.in.count += TT.in.sz;
226       else TT.in.count += n;
227     }
228 
229     TT.out.count = TT.in.count;
230     if (bs) {
231       write_out(1);
232       TT.in.count = 0;
233       continue;
234     }
235 
236     if (TT.in.count >= TT.out.sz) {
237       write_out(0);
238       TT.in.count = TT.out.count;
239     }
240   }
241   if (TT.out.count) write_out(1); //write any remaining input blocks
242   if ((conv&C_FSYNC) && fsync(TT.out.fd)<0)
243     perror_exit("%s: fsync", TT.out.name);
244 
245   close(TT.in.fd);
246   close(TT.out.fd);
247   if (TT.in.buff) free(TT.in.buff);
248 
249   status();
250 }
251