Initial public busybox upstream commit
[busybox4maemo] / 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 <signal.h>  /* For FEATURE_DD_SIGNAL_HANDLING */
12 #include "libbb.h"
13
14 /* This is a NOEXEC applet. Be very careful! */
15
16
17 enum {
18         ifd = STDIN_FILENO,
19         ofd = STDOUT_FILENO,
20 };
21
22 static const struct suffix_mult dd_suffixes[] = {
23         { "c", 1 },
24         { "w", 2 },
25         { "b", 512 },
26         { "kD", 1000 },
27         { "k", 1024 },
28         { "K", 1024 },  /* compat with coreutils dd */
29         { "MD", 1000000 },
30         { "M", 1048576 },
31         { "GD", 1000000000 },
32         { "G", 1073741824 },
33         { }
34 };
35
36 struct globals {
37         off_t out_full, out_part, in_full, in_part;
38 };
39 #define G (*(struct globals*)&bb_common_bufsiz1)
40 /* We have to zero it out because of NOEXEC */
41 #define INIT_G() memset(&G, 0, sizeof(G))
42
43
44 static void dd_output_status(int ATTRIBUTE_UNUSED cur_signal)
45 {
46         /* Deliberately using %u, not %d */
47         fprintf(stderr, "%"OFF_FMT"u+%"OFF_FMT"u records in\n"
48                         "%"OFF_FMT"u+%"OFF_FMT"u records out\n",
49                         G.in_full, G.in_part,
50                         G.out_full, G.out_part);
51 }
52
53 static ssize_t full_write_or_warn(const void *buf, size_t len,
54         const char *const filename)
55 {
56         ssize_t n = full_write(ofd, buf, len);
57         if (n < 0)
58                 bb_perror_msg("writing '%s'", filename);
59         return n;
60 }
61
62 static bool write_and_stats(const void *buf, size_t len, size_t obs,
63         const char *filename)
64 {
65         ssize_t n = full_write_or_warn(buf, len, filename);
66         if (n < 0)
67                 return 1;
68         if (n == obs)
69                 G.out_full++;
70         else if (n) /* > 0 */
71                 G.out_part++;
72         return 0;
73 }
74
75 #if ENABLE_LFS
76 #define XATOU_SFX xatoull_sfx
77 #else
78 #define XATOU_SFX xatoul_sfx
79 #endif
80
81 int dd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
82 int dd_main(int argc, char **argv)
83 {
84         enum {
85                 FLAG_SYNC    = 1 << 0,
86                 FLAG_NOERROR = 1 << 1,
87                 FLAG_NOTRUNC = 1 << 2,
88                 FLAG_TWOBUFS = 1 << 3,
89                 FLAG_COUNT   = 1 << 4,
90         };
91         static const char keywords[] ALIGN1 =
92                 "bs=\0""count=\0""seek=\0""skip=\0""if=\0""of=\0"
93 #if ENABLE_FEATURE_DD_IBS_OBS
94                 "ibs=\0""obs=\0""conv=\0""notrunc\0""sync\0""noerror\0"
95 #endif
96                 ;
97         enum {
98                 OP_bs = 1,
99                 OP_count,
100                 OP_seek,
101                 OP_skip,
102                 OP_if,
103                 OP_of,
104 #if ENABLE_FEATURE_DD_IBS_OBS
105                 OP_ibs,
106                 OP_obs,
107                 OP_conv,
108                 OP_conv_notrunc,
109                 OP_conv_sync,
110                 OP_conv_noerror,
111 #endif
112         };
113         int exitcode = EXIT_FAILURE;
114         size_t ibs = 512, obs = 512;
115         ssize_t n, w;
116         char *ibuf, *obuf;
117         /* And these are all zeroed at once! */
118         struct {
119                 int flags;
120                 size_t oc;
121                 off_t count;
122                 off_t seek, skip;
123                 const char *infile, *outfile;
124         } Z;
125 #define flags   (Z.flags  )
126 #define oc      (Z.oc     )
127 #define count   (Z.count  )
128 #define seek    (Z.seek   )
129 #define skip    (Z.skip   )
130 #define infile  (Z.infile )
131 #define outfile (Z.outfile)
132
133         memset(&Z, 0, sizeof(Z));
134         INIT_G();
135         //fflush(NULL); - is this needed because of NOEXEC?
136
137 #if ENABLE_FEATURE_DD_SIGNAL_HANDLING
138         signal_SA_RESTART_empty_mask(SIGUSR1, dd_output_status);
139 #endif
140
141         for (n = 1; n < argc; n++) {
142                 smalluint key_len;
143                 smalluint what;
144                 char *key;
145                 char *arg = argv[n];
146
147 //XXX:FIXME: we reject plain "dd --" This would cost ~20 bytes, so..
148 //if (*arg == '-' && *++arg == '-' && !*++arg) continue;
149                 key = strstr(arg, "=");
150                 if (key == NULL)
151                         bb_show_usage();
152                 key_len = key - arg + 1;
153                 key = xstrndup(arg, key_len);
154                 what = index_in_strings(keywords, key) + 1;
155                 if (ENABLE_FEATURE_CLEAN_UP)
156                         free(key);
157                 if (what == 0)
158                         bb_show_usage();
159                 arg += key_len;
160 #if ENABLE_FEATURE_DD_IBS_OBS
161                 if (what == OP_ibs) {
162                         /* Must fit into positive ssize_t */
163                         ibs = xatoul_range_sfx(arg, 1, ((size_t)-1L)/2, dd_suffixes);
164                         continue;
165                 }
166                 if (what == OP_obs) {
167                         obs = xatoul_range_sfx(arg, 1, ((size_t)-1L)/2, dd_suffixes);
168                         continue;
169                 }
170                 if (what == OP_conv) {
171                         while (1) {
172                                 /* find ',', replace them with NUL so we can use arg for
173                                  * index_in_strings() without copying.
174                                  * We rely on arg being non-null, else strchr would fault.
175                                  */
176                                 key = strchr(arg, ',');
177                                 if (key)
178                                         *key = '\0';
179                                 what = index_in_strings(keywords, arg) + 1;
180                                 if (what < OP_conv_notrunc)
181                                         bb_error_msg_and_die(bb_msg_invalid_arg, arg, "conv");
182                                 if (what == OP_conv_notrunc)
183                                         flags |= FLAG_NOTRUNC;
184                                 if (what == OP_conv_sync)
185                                         flags |= FLAG_SYNC;
186                                 if (what == OP_conv_noerror)
187                                         flags |= FLAG_NOERROR;
188                                 if (!key) /* no ',' left, so this was the last specifier */
189                                         break;
190                                 arg = key + 1; /* skip this keyword and ',' */
191                         }
192                         continue;
193                 }
194 #endif
195                 if (what == OP_bs) {
196                         ibs = obs = xatoul_range_sfx(arg, 1, ((size_t)-1L)/2, dd_suffixes);
197                         continue;
198                 }
199                 /* These can be large: */
200                 if (what == OP_count) {
201                         flags |= FLAG_COUNT;
202                         count = XATOU_SFX(arg, dd_suffixes);
203                         continue;
204                 }
205                 if (what == OP_seek) {
206                         seek = XATOU_SFX(arg, dd_suffixes);
207                         continue;
208                 }
209                 if (what == OP_skip) {
210                         skip = XATOU_SFX(arg, dd_suffixes);
211                         continue;
212                 }
213                 if (what == OP_if) {
214                         infile = arg;
215                         continue;
216                 }
217                 if (what == OP_of)
218                         outfile = arg;
219         }
220 //XXX:FIXME for huge ibs or obs, malloc'ing them isn't the brightest idea ever
221         ibuf = obuf = xmalloc(ibs);
222         if (ibs != obs) {
223                 flags |= FLAG_TWOBUFS;
224                 obuf = xmalloc(obs);
225         }
226         if (infile != NULL)
227                 xmove_fd(xopen(infile, O_RDONLY), ifd);
228         else {
229                 infile = bb_msg_standard_input;
230         }
231         if (outfile != NULL) {
232                 int oflag = O_WRONLY | O_CREAT;
233
234                 if (!seek && !(flags & FLAG_NOTRUNC))
235                         oflag |= O_TRUNC;
236
237                 xmove_fd(xopen(outfile, oflag), ofd);
238
239                 if (seek && !(flags & FLAG_NOTRUNC)) {
240                         if (ftruncate(ofd, seek * obs) < 0) {
241                                 struct stat st;
242
243                                 if (fstat(ofd, &st) < 0 || S_ISREG(st.st_mode) ||
244                                                 S_ISDIR(st.st_mode))
245                                         goto die_outfile;
246                         }
247                 }
248         } else {
249                 outfile = bb_msg_standard_output;
250         }
251         if (skip) {
252                 if (lseek(ifd, skip * ibs, SEEK_CUR) < 0) {
253                         while (skip-- > 0) {
254                                 n = safe_read(ifd, ibuf, ibs);
255                                 if (n < 0)
256                                         goto die_infile;
257                                 if (n == 0)
258                                         break;
259                         }
260                 }
261         }
262         if (seek) {
263                 if (lseek(ofd, seek * obs, SEEK_CUR) < 0)
264                         goto die_outfile;
265         }
266
267         while (!(flags & FLAG_COUNT) || (G.in_full + G.in_part != count)) {
268                 if (flags & FLAG_NOERROR) /* Pre-zero the buffer if conv=noerror */
269                         memset(ibuf, 0, ibs);
270                 n = safe_read(ifd, ibuf, ibs);
271                 if (n == 0)
272                         break;
273                 if (n < 0) {
274                         if (!(flags & FLAG_NOERROR))
275                                 goto die_infile;
276                         n = ibs;
277                         bb_simple_perror_msg(infile);
278                 }
279                 if ((size_t)n == ibs)
280                         G.in_full++;
281                 else {
282                         G.in_part++;
283                         if (flags & FLAG_SYNC) {
284                                 memset(ibuf + n, '\0', ibs - n);
285                                 n = ibs;
286                         }
287                 }
288                 if (flags & FLAG_TWOBUFS) {
289                         char *tmp = ibuf;
290                         while (n) {
291                                 size_t d = obs - oc;
292
293                                 if (d > n)
294                                         d = n;
295                                 memcpy(obuf + oc, tmp, d);
296                                 n -= d;
297                                 tmp += d;
298                                 oc += d;
299                                 if (oc == obs) {
300                                         if (write_and_stats(obuf, obs, obs, outfile))
301                                                 goto out_status;
302                                         oc = 0;
303                                 }
304                         }
305                 } else if (write_and_stats(ibuf, n, obs, outfile))
306                         goto out_status;
307         }
308
309         if (ENABLE_FEATURE_DD_IBS_OBS && oc) {
310                 w = full_write_or_warn(obuf, oc, outfile);
311                 if (w < 0) goto out_status;
312                 if (w > 0)
313                         G.out_part++;
314         }
315         if (close(ifd) < 0) {
316  die_infile:
317                 bb_simple_perror_msg_and_die(infile);
318         }
319
320         if (close(ofd) < 0) {
321  die_outfile:
322                 bb_simple_perror_msg_and_die(outfile);
323         }
324
325         exitcode = EXIT_SUCCESS;
326  out_status:
327         dd_output_status(0);
328
329         return exitcode;
330 }