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