7c1a0c0df12869688ead0da839b886ef01e5103c
[platform/upstream/busybox.git] / coreutils / dd.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * Mini dd implementation for busybox
4  *
5  *
6  * Copyright (C) 2000,2001  Matt Kraai
7  *
8  * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
9  */
10
11 #include "libbb.h"
12
13 /* This is a NOEXEC applet. Be very careful! */
14
15
16 enum {
17         ifd = STDIN_FILENO,
18         ofd = STDOUT_FILENO,
19 };
20
21 static const struct suffix_mult dd_suffixes[] = {
22         { "c", 1 },
23         { "w", 2 },
24         { "b", 512 },
25         { "kD", 1000 },
26         { "k", 1024 },
27         { "K", 1024 },  /* compat with coreutils dd */
28         { "MD", 1000000 },
29         { "M", 1048576 },
30         { "GD", 1000000000 },
31         { "G", 1073741824 },
32         { "", 0 }
33 };
34
35 struct globals {
36         off_t out_full, out_part, in_full, in_part;
37 #if ENABLE_FEATURE_DD_THIRD_STATUS_LINE
38         unsigned long long total_bytes;
39         unsigned long long begin_time_us;
40 #endif
41 } FIX_ALIASING;
42 #define G (*(struct globals*)&bb_common_bufsiz1)
43 #define INIT_G() do { \
44         /* we have to zero it out because of NOEXEC */ \
45         memset(&G, 0, sizeof(G)); \
46 } while (0)
47
48
49 static void dd_output_status(int UNUSED_PARAM cur_signal)
50 {
51 #if ENABLE_FEATURE_DD_THIRD_STATUS_LINE
52         double seconds;
53         unsigned long long bytes_sec;
54         unsigned long long now_us = monotonic_us(); /* before fprintf */
55 #endif
56
57         /* Deliberately using %u, not %d */
58         fprintf(stderr, "%"OFF_FMT"u+%"OFF_FMT"u records in\n"
59                         "%"OFF_FMT"u+%"OFF_FMT"u records out\n",
60                         G.in_full, G.in_part,
61                         G.out_full, G.out_part);
62
63 #if ENABLE_FEATURE_DD_THIRD_STATUS_LINE
64         fprintf(stderr, "%llu bytes (%sB) copied, ",
65                         G.total_bytes,
66                         /* show fractional digit, use suffixes */
67                         make_human_readable_str(G.total_bytes, 1, 0)
68         );
69         /* Corner cases:
70          * ./busybox dd </dev/null >/dev/null
71          * ./busybox dd bs=1M count=2000 </dev/zero >/dev/null
72          * (echo DONE) | ./busybox dd >/dev/null
73          * (sleep 1; echo DONE) | ./busybox dd >/dev/null
74          */
75         seconds = (now_us - G.begin_time_us) / 1000000.0;
76         bytes_sec = G.total_bytes / seconds;
77         fprintf(stderr, "%f seconds, %sB/s\n",
78                         seconds,
79                         /* show fractional digit, use suffixes */
80                         make_human_readable_str(bytes_sec, 1, 0)
81         );
82 #endif
83 }
84
85 static ssize_t full_write_or_warn(const void *buf, size_t len,
86         const char *const filename)
87 {
88         ssize_t n = full_write(ofd, buf, len);
89         if (n < 0)
90                 bb_perror_msg("writing '%s'", filename);
91         return n;
92 }
93
94 static bool write_and_stats(const void *buf, size_t len, size_t obs,
95         const char *filename)
96 {
97         ssize_t n = full_write_or_warn(buf, len, filename);
98         if (n < 0)
99                 return 1;
100         if ((size_t)n == obs)
101                 G.out_full++;
102         else if (n) /* > 0 */
103                 G.out_part++;
104 #if ENABLE_FEATURE_DD_THIRD_STATUS_LINE
105         G.total_bytes += n;
106 #endif
107         return 0;
108 }
109
110 #if ENABLE_LFS
111 # define XATOU_SFX xatoull_sfx
112 #else
113 # define XATOU_SFX xatoul_sfx
114 #endif
115
116 int dd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
117 int dd_main(int argc UNUSED_PARAM, char **argv)
118 {
119         enum {
120                 /* Must be in the same order as OP_conv_XXX! */
121                 /* (see "flags |= (1 << what)" below) */
122                 FLAG_NOTRUNC = 1 << 0,
123                 FLAG_SYNC    = 1 << 1,
124                 FLAG_NOERROR = 1 << 2,
125                 FLAG_FSYNC   = 1 << 3,
126                 /* end of conv flags */
127                 FLAG_TWOBUFS = 1 << 4,
128                 FLAG_COUNT   = 1 << 5,
129         };
130         static const char keywords[] ALIGN1 =
131                 "bs\0""count\0""seek\0""skip\0""if\0""of\0"
132 #if ENABLE_FEATURE_DD_IBS_OBS
133                 "ibs\0""obs\0""conv\0"
134 #endif
135                 ;
136 #if ENABLE_FEATURE_DD_IBS_OBS
137         static const char conv_words[] ALIGN1 =
138                 "notrunc\0""sync\0""noerror\0""fsync\0";
139 #endif
140         enum {
141                 OP_bs = 0,
142                 OP_count,
143                 OP_seek,
144                 OP_skip,
145                 OP_if,
146                 OP_of,
147 #if ENABLE_FEATURE_DD_IBS_OBS
148                 OP_ibs,
149                 OP_obs,
150                 OP_conv,
151                 /* Must be in the same order as FLAG_XXX! */
152                 OP_conv_notrunc = 0,
153                 OP_conv_sync,
154                 OP_conv_noerror,
155                 OP_conv_fsync,
156         /* Unimplemented conv=XXX: */
157         //nocreat       do not create the output file
158         //excl          fail if the output file already exists
159         //fdatasync     physically write output file data before finishing
160         //swab          swap every pair of input bytes
161         //lcase         change upper case to lower case
162         //ucase         change lower case to upper case
163         //block         pad newline-terminated records with spaces to cbs-size
164         //unblock       replace trailing spaces in cbs-size records with newline
165         //ascii         from EBCDIC to ASCII
166         //ebcdic        from ASCII to EBCDIC
167         //ibm           from ASCII to alternate EBCDIC
168 #endif
169         };
170         int exitcode = EXIT_FAILURE;
171         size_t ibs = 512, obs = 512;
172         ssize_t n, w;
173         char *ibuf, *obuf;
174         /* And these are all zeroed at once! */
175         struct {
176                 int flags;
177                 size_t oc;
178                 off_t count;
179                 off_t seek, skip;
180                 const char *infile, *outfile;
181         } Z;
182 #define flags   (Z.flags  )
183 #define oc      (Z.oc     )
184 #define count   (Z.count  )
185 #define seek    (Z.seek   )
186 #define skip    (Z.skip   )
187 #define infile  (Z.infile )
188 #define outfile (Z.outfile)
189
190         memset(&Z, 0, sizeof(Z));
191         INIT_G();
192         //fflush_all(); - is this needed because of NOEXEC?
193
194         for (n = 1; argv[n]; n++) {
195                 int what;
196                 char *val;
197                 char *arg = argv[n];
198
199 #if ENABLE_DESKTOP
200                 /* "dd --". NB: coreutils 6.9 will complain if they see
201                  * more than one of them. We wouldn't. */
202                 if (arg[0] == '-' && arg[1] == '-' && arg[2] == '\0')
203                         continue;
204 #endif
205                 val = strchr(arg, '=');
206                 if (val == NULL)
207                         bb_show_usage();
208                 *val = '\0';
209                 what = index_in_strings(keywords, arg);
210                 if (what < 0)
211                         bb_show_usage();
212                 /* *val = '='; - to preserve ps listing? */
213                 val++;
214 #if ENABLE_FEATURE_DD_IBS_OBS
215                 if (what == OP_ibs) {
216                         /* Must fit into positive ssize_t */
217                         ibs = xatoul_range_sfx(val, 1, ((size_t)-1L)/2, dd_suffixes);
218                         /*continue;*/
219                 }
220                 if (what == OP_obs) {
221                         obs = xatoul_range_sfx(val, 1, ((size_t)-1L)/2, dd_suffixes);
222                         /*continue;*/
223                 }
224                 if (what == OP_conv) {
225                         while (1) {
226                                 /* find ',', replace them with NUL so we can use val for
227                                  * index_in_strings() without copying.
228                                  * We rely on val being non-null, else strchr would fault.
229                                  */
230                                 arg = strchr(val, ',');
231                                 if (arg)
232                                         *arg = '\0';
233                                 what = index_in_strings(conv_words, val);
234                                 if (what < 0)
235                                         bb_error_msg_and_die(bb_msg_invalid_arg, val, "conv");
236                                 flags |= (1 << what);
237                                 if (!arg) /* no ',' left, so this was the last specifier */
238                                         break;
239                                 /* *arg = ','; - to preserve ps listing? */
240                                 val = arg + 1; /* skip this keyword and ',' */
241                         }
242                         continue; /* we trashed 'what', can't fall through */
243                 }
244 #endif
245                 if (what == OP_bs) {
246                         ibs = obs = xatoul_range_sfx(val, 1, ((size_t)-1L)/2, dd_suffixes);
247                         /*continue;*/
248                 }
249                 /* These can be large: */
250                 if (what == OP_count) {
251                         flags |= FLAG_COUNT;
252                         count = XATOU_SFX(val, dd_suffixes);
253                         /*continue;*/
254                 }
255                 if (what == OP_seek) {
256                         seek = XATOU_SFX(val, dd_suffixes);
257                         /*continue;*/
258                 }
259                 if (what == OP_skip) {
260                         skip = XATOU_SFX(val, dd_suffixes);
261                         /*continue;*/
262                 }
263                 if (what == OP_if) {
264                         infile = val;
265                         /*continue;*/
266                 }
267                 if (what == OP_of) {
268                         outfile = val;
269                         /*continue;*/
270                 }
271         } /* end of "for (argv[n])" */
272
273 //XXX:FIXME for huge ibs or obs, malloc'ing them isn't the brightest idea ever
274         ibuf = obuf = xmalloc(ibs);
275         if (ibs != obs) {
276                 flags |= FLAG_TWOBUFS;
277                 obuf = xmalloc(obs);
278         }
279
280 #if ENABLE_FEATURE_DD_SIGNAL_HANDLING
281         signal_SA_RESTART_empty_mask(SIGUSR1, dd_output_status);
282 #endif
283 #if ENABLE_FEATURE_DD_THIRD_STATUS_LINE
284         G.begin_time_us = monotonic_us();
285 #endif
286
287         if (infile != NULL)
288                 xmove_fd(xopen(infile, O_RDONLY), ifd);
289         else {
290                 infile = bb_msg_standard_input;
291         }
292         if (outfile != NULL) {
293                 int oflag = O_WRONLY | O_CREAT;
294
295                 if (!seek && !(flags & FLAG_NOTRUNC))
296                         oflag |= O_TRUNC;
297
298                 xmove_fd(xopen(outfile, oflag), ofd);
299
300                 if (seek && !(flags & FLAG_NOTRUNC)) {
301                         if (ftruncate(ofd, seek * obs) < 0) {
302                                 struct stat st;
303
304                                 if (fstat(ofd, &st) < 0
305                                  || S_ISREG(st.st_mode)
306                                  || S_ISDIR(st.st_mode)
307                                 ) {
308                                         goto die_outfile;
309                                 }
310                         }
311                 }
312         } else {
313                 outfile = bb_msg_standard_output;
314         }
315         if (skip) {
316                 if (lseek(ifd, skip * ibs, SEEK_CUR) < 0) {
317                         while (skip-- > 0) {
318                                 n = safe_read(ifd, ibuf, ibs);
319                                 if (n < 0)
320                                         goto die_infile;
321                                 if (n == 0)
322                                         break;
323                         }
324                 }
325         }
326         if (seek) {
327                 if (lseek(ofd, seek * obs, SEEK_CUR) < 0)
328                         goto die_outfile;
329         }
330
331         while (!(flags & FLAG_COUNT) || (G.in_full + G.in_part != count)) {
332                 n = safe_read(ifd, ibuf, ibs);
333                 if (n == 0)
334                         break;
335                 if (n < 0) {
336                         /* "Bad block" */
337                         if (!(flags & FLAG_NOERROR))
338                                 goto die_infile;
339                         bb_simple_perror_msg(infile);
340                         /* GNU dd with conv=noerror skips over bad blocks */
341                         xlseek(ifd, ibs, SEEK_CUR);
342                         /* conv=noerror,sync writes NULs,
343                          * conv=noerror just ignores input bad blocks */
344                         n = 0;
345                 }
346                 if ((size_t)n == ibs)
347                         G.in_full++;
348                 else {
349                         G.in_part++;
350                         if (flags & FLAG_SYNC) {
351                                 memset(ibuf + n, 0, ibs - n);
352                                 n = ibs;
353                         }
354                 }
355                 if (flags & FLAG_TWOBUFS) {
356                         char *tmp = ibuf;
357                         while (n) {
358                                 size_t d = obs - oc;
359
360                                 if (d > (size_t)n)
361                                         d = n;
362                                 memcpy(obuf + oc, tmp, d);
363                                 n -= d;
364                                 tmp += d;
365                                 oc += d;
366                                 if (oc == obs) {
367                                         if (write_and_stats(obuf, obs, obs, outfile))
368                                                 goto out_status;
369                                         oc = 0;
370                                 }
371                         }
372                 } else if (write_and_stats(ibuf, n, obs, outfile))
373                         goto out_status;
374
375                 if (flags & FLAG_FSYNC) {
376                         if (fsync(ofd) < 0)
377                                 goto die_outfile;
378                 }
379         }
380
381         if (ENABLE_FEATURE_DD_IBS_OBS && oc) {
382                 w = full_write_or_warn(obuf, oc, outfile);
383                 if (w < 0) goto out_status;
384                 if (w > 0) G.out_part++;
385         }
386         if (close(ifd) < 0) {
387  die_infile:
388                 bb_simple_perror_msg_and_die(infile);
389         }
390
391         if (close(ofd) < 0) {
392  die_outfile:
393                 bb_simple_perror_msg_and_die(outfile);
394         }
395
396         exitcode = EXIT_SUCCESS;
397  out_status:
398         dd_output_status(0);
399
400         return exitcode;
401 }