blob: 797aabdd931ad1ecca395ad743a0987a34db8acd [file] [log] [blame]
Erik Andersene49d5ec2000-02-08 19:58:47 +00001/* vi: set sw=4 ts=4: */
Eric Andersencc8ed391999-10-05 16:24:54 +00002/*
Eric Andersenc4996011999-10-20 22:08:37 +00003 * Mini dd implementation for busybox
4 *
Eric Andersenc4996011999-10-20 22:08:37 +00005 *
Matt Kraai3eeab3b2001-12-07 16:27:37 +00006 * Copyright (C) 2000,2001 Matt Kraai
Eric Andersenc4996011999-10-20 22:08:37 +00007 *
Bernhard Reutner-Fischerc2cb0f32006-04-13 12:45:04 +00008 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
Eric Andersencc8ed391999-10-05 16:24:54 +00009 */
10
Bernhard Reutner-Fischer73561cc2006-08-28 23:31:54 +000011#include <signal.h> /* For FEATURE_DD_SIGNAL_HANDLING */
Denis Vlasenko99912ca2007-04-10 15:43:37 +000012#include "busybox.h"
13
14/* This is a NOEXEC applet. Be very careful! */
15
Eric Andersencbe31da2001-02-20 06:14:08 +000016
Matt Kraaia164c642001-02-05 17:50:03 +000017static const struct suffix_mult dd_suffixes[] = {
Matt Kraai24ac0172000-12-18 21:38:57 +000018 { "c", 1 },
19 { "w", 2 },
20 { "b", 512 },
21 { "kD", 1000 },
22 { "k", 1024 },
Bernhard Reutner-Fischer2a47dea2007-04-04 14:01:23 +000023 { "K", 1024 }, /* compat with coreutils dd */
Matt Kraai24ac0172000-12-18 21:38:57 +000024 { "MD", 1000000 },
25 { "M", 1048576 },
26 { "GD", 1000000000 },
27 { "G", 1073741824 },
28 { NULL, 0 }
29};
30
Bernhard Reutner-Fischer2a47dea2007-04-04 14:01:23 +000031struct globals {
32 off_t out_full, out_part, in_full, in_part;
33};
34#define G (*(struct globals*)&bb_common_bufsiz1)
Rob Landleyc5598172006-05-02 22:44:04 +000035
Bernhard Reutner-Fischer73561cc2006-08-28 23:31:54 +000036static void dd_output_status(int ATTRIBUTE_UNUSED cur_signal)
Rob Landleyc5598172006-05-02 22:44:04 +000037{
Denis Vlasenkocf30cc82006-11-24 14:53:18 +000038 fprintf(stderr, "%"OFF_FMT"d+%"OFF_FMT"d records in\n"
39 "%"OFF_FMT"d+%"OFF_FMT"d records out\n",
Bernhard Reutner-Fischer2a47dea2007-04-04 14:01:23 +000040 G.in_full, G.in_part,
41 G.out_full, G.out_part);
Rob Landleyc5598172006-05-02 22:44:04 +000042}
43
Denis Vlasenko3b8ff682006-10-31 15:55:56 +000044static ssize_t full_write_or_warn(int fd, const void *buf, size_t len,
Bernhard Reutner-Fischer2a47dea2007-04-04 14:01:23 +000045 const char * const filename)
Denis Vlasenko3b8ff682006-10-31 15:55:56 +000046{
47 ssize_t n = full_write(fd, buf, len);
48 if (n < 0)
49 bb_perror_msg("writing '%s'", filename);
50 return n;
51}
52
Bernhard Reutner-Fischer2a47dea2007-04-04 14:01:23 +000053static bool write_and_stats(int fd, const void *buf, size_t len, size_t obs,
54 const char * const filename)
55{
56 ssize_t n = full_write_or_warn(fd, buf, len, filename);
57 if (n < 0)
58 return 1;
59 if (n == obs)
60 G.out_full++;
Denis Vlasenkod1801a42007-04-19 20:08:19 +000061 else if (n) /* > 0 */
Bernhard Reutner-Fischer2a47dea2007-04-04 14:01:23 +000062 G.out_part++;
63 return 0;
64}
65
Denis Vlasenko5dd27b12006-11-25 14:46:21 +000066#if ENABLE_LFS
67#define XATOU_SFX xatoull_sfx
68#else
69#define XATOU_SFX xatoul_sfx
70#endif
71
Denis Vlasenko06af2162007-02-03 17:28:39 +000072int dd_main(int argc, char **argv);
Matt Kraai24ac0172000-12-18 21:38:57 +000073int dd_main(int argc, char **argv)
Eric Andersen3cf52d11999-10-12 22:26:06 +000074{
Denis Vlasenko3b8ff682006-10-31 15:55:56 +000075 enum {
Denis Vlasenkod1801a42007-04-19 20:08:19 +000076 SYNC_FLAG = 1 << 0,
77 NOERROR = 1 << 1,
78 TRUNC_FLAG = 1 << 2,
79 TWOBUFS_FLAG = 1 << 3,
Denis Vlasenko3b8ff682006-10-31 15:55:56 +000080 };
Denis Vlasenko240a1cf2007-04-08 16:07:02 +000081 static const char * const keywords[] = {
Bernhard Reutner-Fischer2a47dea2007-04-04 14:01:23 +000082 "bs=", "count=", "seek=", "skip=", "if=", "of=",
83#if ENABLE_FEATURE_DD_IBS_OBS
84 "ibs=", "obs=", "conv=", "notrunc", "sync", "noerror",
85#endif
86 NULL
87 };
Bernhard Reutner-Fischer99003b82007-04-10 19:30:50 +000088 enum {
89 OP_bs = 1,
90 OP_count,
91 OP_seek,
92 OP_skip,
93 OP_if,
94 OP_of,
Bernhard Reutner-Fischere468ef22007-04-16 12:21:05 +000095#if ENABLE_FEATURE_DD_IBS_OBS
Bernhard Reutner-Fischer99003b82007-04-10 19:30:50 +000096 OP_ibs,
97 OP_obs,
98 OP_conv,
99 OP_conv_notrunc,
100 OP_conv_sync,
Denis Vlasenkod1801a42007-04-19 20:08:19 +0000101 OP_conv_NOERROR,
Bernhard Reutner-Fischere468ef22007-04-16 12:21:05 +0000102#endif
Bernhard Reutner-Fischer99003b82007-04-10 19:30:50 +0000103 };
Denis Vlasenkod1801a42007-04-19 20:08:19 +0000104 int flags = TRUNC_FLAG;
Denis Vlasenko56254152006-10-07 16:24:46 +0000105 size_t oc = 0, ibs = 512, obs = 512;
Denis Vlasenko3b8ff682006-10-31 15:55:56 +0000106 ssize_t n, w;
Denis Vlasenko7039a662006-10-08 17:54:47 +0000107 off_t seek = 0, skip = 0, count = OFF_T_MAX;
Bernhard Reutner-Fischer2a47dea2007-04-04 14:01:23 +0000108 int ifd, ofd;
Rob Landley2686d3b2006-05-16 16:52:12 +0000109 const char *infile = NULL, *outfile = NULL;
110 char *ibuf, *obuf;
Eric Andersenddea3682000-11-29 22:33:02 +0000111
Bernhard Reutner-Fischer73561cc2006-08-28 23:31:54 +0000112 if (ENABLE_FEATURE_DD_SIGNAL_HANDLING) {
Rob Landleyc5598172006-05-02 22:44:04 +0000113 struct sigaction sa;
114
115 memset(&sa, 0, sizeof(sa));
Bernhard Reutner-Fischer73561cc2006-08-28 23:31:54 +0000116 sa.sa_handler = dd_output_status;
Rob Landleyc5598172006-05-02 22:44:04 +0000117 sa.sa_flags = SA_RESTART;
118 sigemptyset(&sa.sa_mask);
Bernhard Reutner-Fischer73561cc2006-08-28 23:31:54 +0000119 sigaction(SIGUSR1, &sa, 0);
Rob Landleyc5598172006-05-02 22:44:04 +0000120 }
121
Bernhard Reutner-Fischer73561cc2006-08-28 23:31:54 +0000122 for (n = 1; n < argc; n++) {
Bernhard Reutner-Fischer99003b82007-04-10 19:30:50 +0000123 smalluint key_len;
124 smalluint what;
Bernhard Reutner-Fischer2a47dea2007-04-04 14:01:23 +0000125 char *key;
Denis Vlasenko3b8ff682006-10-31 15:55:56 +0000126 char *arg = argv[n];
Denis Vlasenko3b8ff682006-10-31 15:55:56 +0000127
Bernhard Reutner-Fischer2a47dea2007-04-04 14:01:23 +0000128//XXX:FIXME: we reject plain "dd --" This would cost ~20 bytes, so..
129//if (*arg == '-' && *++arg == '-' && !*++arg) continue;
130 key = strstr(arg, "=");
131 if (key == NULL)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000132 bb_show_usage();
Bernhard Reutner-Fischer2a47dea2007-04-04 14:01:23 +0000133 key_len = key - arg + 1;
134 key = xstrndup(arg, key_len);
135 what = index_in_str_array(keywords, key) + 1;
136 if (ENABLE_FEATURE_CLEAN_UP)
137 free(key);
138 if (what == 0)
139 bb_show_usage();
140 arg += key_len;
141 /* Must fit into positive ssize_t */
Bernhard Reutner-Fischere468ef22007-04-16 12:21:05 +0000142#if ENABLE_FEATURE_DD_IBS_OBS
Bernhard Reutner-Fischer2a47dea2007-04-04 14:01:23 +0000143 if (what == OP_ibs) {
144 ibs = xatoul_range_sfx(arg, 1, ((size_t)-1L)/2, dd_suffixes);
145 continue;
146 }
147 if (what == OP_obs) {
148 obs = xatoul_range_sfx(arg, 1, ((size_t)-1L)/2, dd_suffixes);
149 continue;
150 }
151 if (what == OP_conv) {
152 while (1) {
153 /* find ',', replace them with nil so we can use arg for
154 * index_in_str_array without copying.
Denis Vlasenkod1801a42007-04-19 20:08:19 +0000155 * We rely on arg being non-null, else strchr would fault.
Bernhard Reutner-Fischer2a47dea2007-04-04 14:01:23 +0000156 */
Denis Vlasenkod1801a42007-04-19 20:08:19 +0000157 key = strchr(arg, ',');
Bernhard Reutner-Fischer2a47dea2007-04-04 14:01:23 +0000158 if (key)
159 *key = '\0';
160 what = index_in_str_array(keywords, arg) + 1;
161 if (what < OP_conv_notrunc)
162 bb_error_msg_and_die(bb_msg_invalid_arg, arg, "conv");
163 if (what == OP_conv_notrunc)
Denis Vlasenkod1801a42007-04-19 20:08:19 +0000164 flags &= ~TRUNC_FLAG;
Bernhard Reutner-Fischer2a47dea2007-04-04 14:01:23 +0000165 if (what == OP_conv_sync)
Denis Vlasenkod1801a42007-04-19 20:08:19 +0000166 flags |= SYNC_FLAG;
167 if (what == OP_conv_NOERROR)
168 flags |= NOERROR;
Bernhard Reutner-Fischer2a47dea2007-04-04 14:01:23 +0000169 if (!key) /* no ',' left, so this was the last specifier */
170 break;
Denis Vlasenkod1801a42007-04-19 20:08:19 +0000171 arg = key + 1; /* skip this keyword and ',' */
Bernhard Reutner-Fischer2a47dea2007-04-04 14:01:23 +0000172 }
173 continue;
174 }
Bernhard Reutner-Fischere468ef22007-04-16 12:21:05 +0000175#endif
Bernhard Reutner-Fischer2a47dea2007-04-04 14:01:23 +0000176 if (what == OP_bs) {
177 ibs = obs = xatoul_range_sfx(arg, 1, ((size_t)-1L)/2, dd_suffixes);
178 continue;
179 }
180 /* These can be large: */
181 if (what == OP_count) {
182 count = XATOU_SFX(arg, dd_suffixes);
183 continue;
184 }
185 if (what == OP_seek) {
186 seek = XATOU_SFX(arg, dd_suffixes);
187 continue;
188 }
Denis Vlasenkod1801a42007-04-19 20:08:19 +0000189 if (what == OP_skip) {
Bernhard Reutner-Fischer2a47dea2007-04-04 14:01:23 +0000190 skip = XATOU_SFX(arg, dd_suffixes);
191 continue;
192 }
193 if (what == OP_if) {
194 infile = arg;
195 continue;
196 }
197 if (what == OP_of)
198 outfile = arg;
Eric Andersen3cf52d11999-10-12 22:26:06 +0000199 }
Bernhard Reutner-Fischer2a47dea2007-04-04 14:01:23 +0000200//XXX:FIXME for huge ibs or obs, malloc'ing them isn't the brightest idea ever
Denis Vlasenko3b8ff682006-10-31 15:55:56 +0000201 ibuf = obuf = xmalloc(ibs);
202 if (ibs != obs) {
Denis Vlasenkod1801a42007-04-19 20:08:19 +0000203 flags |= TWOBUFS_FLAG;
Bernhard Reutner-Fischer73561cc2006-08-28 23:31:54 +0000204 obuf = xmalloc(obs);
Denis Vlasenko3b8ff682006-10-31 15:55:56 +0000205 }
Bernhard Reutner-Fischer73561cc2006-08-28 23:31:54 +0000206 if (infile != NULL)
Denis Vlasenko7039a662006-10-08 17:54:47 +0000207 ifd = xopen(infile, O_RDONLY);
Bernhard Reutner-Fischer73561cc2006-08-28 23:31:54 +0000208 else {
Matt Kraai24ac0172000-12-18 21:38:57 +0000209 ifd = STDIN_FILENO;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000210 infile = bb_msg_standard_input;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000211 }
Matt Kraai24ac0172000-12-18 21:38:57 +0000212 if (outfile != NULL) {
Bernhard Reutner-Fischer2a47dea2007-04-04 14:01:23 +0000213 int oflag = O_WRONLY | O_CREAT;
Matt Kraaic9acf8c2001-01-17 00:21:05 +0000214
Denis Vlasenkod1801a42007-04-19 20:08:19 +0000215 if (!seek && (flags & TRUNC_FLAG))
Matt Kraaic9acf8c2001-01-17 00:21:05 +0000216 oflag |= O_TRUNC;
217
Denis Vlasenkocf749bc2006-11-26 15:45:17 +0000218 ofd = xopen(outfile, oflag);
Matt Kraaic9acf8c2001-01-17 00:21:05 +0000219
Denis Vlasenkod1801a42007-04-19 20:08:19 +0000220 if (seek && (flags & TRUNC_FLAG)) {
Rob Landley2686d3b2006-05-16 16:52:12 +0000221 if (ftruncate(ofd, seek * obs) < 0) {
Matt Kraaieb834782002-02-05 15:28:54 +0000222 struct stat st;
223
Bernhard Reutner-Fischer73561cc2006-08-28 23:31:54 +0000224 if (fstat(ofd, &st) < 0 || S_ISREG(st.st_mode) ||
225 S_ISDIR(st.st_mode))
226 goto die_outfile;
Matt Kraaieb834782002-02-05 15:28:54 +0000227 }
Matt Kraaic9acf8c2001-01-17 00:21:05 +0000228 }
Matt Kraai24ac0172000-12-18 21:38:57 +0000229 } else {
230 ofd = STDOUT_FILENO;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000231 outfile = bb_msg_standard_output;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000232 }
Matt Kraai24ac0172000-12-18 21:38:57 +0000233 if (skip) {
Denis Vlasenko7039a662006-10-08 17:54:47 +0000234 if (lseek(ifd, skip * ibs, SEEK_CUR) < 0) {
Rob Landley2686d3b2006-05-16 16:52:12 +0000235 while (skip-- > 0) {
236 n = safe_read(ifd, ibuf, ibs);
237 if (n < 0)
Bernhard Reutner-Fischer2a47dea2007-04-04 14:01:23 +0000238 goto die_infile;
Rob Landley2686d3b2006-05-16 16:52:12 +0000239 if (n == 0)
240 break;
241 }
Glenn L McGratheaed78a2002-11-28 11:05:28 +0000242 }
Matt Kraai24ac0172000-12-18 21:38:57 +0000243 }
Matt Kraai24ac0172000-12-18 21:38:57 +0000244 if (seek) {
Denis Vlasenko7039a662006-10-08 17:54:47 +0000245 if (lseek(ofd, seek * obs, SEEK_CUR) < 0)
Bernhard Reutner-Fischer73561cc2006-08-28 23:31:54 +0000246 goto die_outfile;
Matt Kraai24ac0172000-12-18 21:38:57 +0000247 }
Eric Andersenddea3682000-11-29 22:33:02 +0000248
Bernhard Reutner-Fischer2a47dea2007-04-04 14:01:23 +0000249 while (G.in_full + G.in_part != count) {
Denis Vlasenkod1801a42007-04-19 20:08:19 +0000250 if (flags & NOERROR) /* Pre-zero the buffer when for NOERROR */
Rob Landley2686d3b2006-05-16 16:52:12 +0000251 memset(ibuf, '\0', ibs);
Rob Landley2686d3b2006-05-16 16:52:12 +0000252 n = safe_read(ifd, ibuf, ibs);
Bernhard Reutner-Fischer73561cc2006-08-28 23:31:54 +0000253 if (n == 0)
Rob Landley2686d3b2006-05-16 16:52:12 +0000254 break;
Eric Andersenef38b392002-04-27 01:31:43 +0000255 if (n < 0) {
Denis Vlasenkod1801a42007-04-19 20:08:19 +0000256 if (flags & NOERROR) {
Rob Landley2686d3b2006-05-16 16:52:12 +0000257 n = ibs;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000258 bb_perror_msg("%s", infile);
Bernhard Reutner-Fischer73561cc2006-08-28 23:31:54 +0000259 } else
Bernhard Reutner-Fischer2a47dea2007-04-04 14:01:23 +0000260 goto die_infile;
Eric Andersenef38b392002-04-27 01:31:43 +0000261 }
Bernhard Reutner-Fischer73561cc2006-08-28 23:31:54 +0000262 if ((size_t)n == ibs)
Bernhard Reutner-Fischer2a47dea2007-04-04 14:01:23 +0000263 G.in_full++;
Bernhard Reutner-Fischer73561cc2006-08-28 23:31:54 +0000264 else {
Bernhard Reutner-Fischer2a47dea2007-04-04 14:01:23 +0000265 G.in_part++;
Denis Vlasenkod1801a42007-04-19 20:08:19 +0000266 if (flags & SYNC_FLAG) {
Rob Landley2686d3b2006-05-16 16:52:12 +0000267 memset(ibuf + n, '\0', ibs - n);
268 n = ibs;
269 }
Glenn L McGratheaed78a2002-11-28 11:05:28 +0000270 }
Denis Vlasenkod1801a42007-04-19 20:08:19 +0000271 if (flags & TWOBUFS_FLAG) {
Rob Landley2686d3b2006-05-16 16:52:12 +0000272 char *tmp = ibuf;
273 while (n) {
274 size_t d = obs - oc;
275
Bernhard Reutner-Fischer73561cc2006-08-28 23:31:54 +0000276 if (d > n)
277 d = n;
Rob Landley2686d3b2006-05-16 16:52:12 +0000278 memcpy(obuf + oc, tmp, d);
279 n -= d;
280 tmp += d;
281 oc += d;
282 if (oc == obs) {
Bernhard Reutner-Fischer2a47dea2007-04-04 14:01:23 +0000283 if (write_and_stats(ofd, obuf, obs, obs, outfile))
284 goto out_status;
Rob Landley2686d3b2006-05-16 16:52:12 +0000285 oc = 0;
286 }
287 }
Denis Vlasenkod1801a42007-04-19 20:08:19 +0000288 } else if (write_and_stats(ofd, ibuf, n, obs, outfile))
289 goto out_status;
Glenn L McGrathf0b073f2000-09-11 00:32:13 +0000290 }
Denis Vlasenko9213a9e2006-09-17 16:28:10 +0000291
Rob Landley2686d3b2006-05-16 16:52:12 +0000292 if (ENABLE_FEATURE_DD_IBS_OBS && oc) {
Denis Vlasenko3b8ff682006-10-31 15:55:56 +0000293 w = full_write_or_warn(ofd, obuf, oc, outfile);
294 if (w < 0) goto out_status;
295 if (w > 0)
Bernhard Reutner-Fischer2a47dea2007-04-04 14:01:23 +0000296 G.out_part++;
Rob Landley2686d3b2006-05-16 16:52:12 +0000297 }
Denis Vlasenko3b8ff682006-10-31 15:55:56 +0000298 if (close(ifd) < 0) {
Bernhard Reutner-Fischer2a47dea2007-04-04 14:01:23 +0000299die_infile:
Manuel Novoa III cad53642003-03-19 09:13:01 +0000300 bb_perror_msg_and_die("%s", infile);
Glenn L McGratheaed78a2002-11-28 11:05:28 +0000301 }
Matt Kraai3eeab3b2001-12-07 16:27:37 +0000302
Denis Vlasenko3b8ff682006-10-31 15:55:56 +0000303 if (close(ofd) < 0) {
Bernhard Reutner-Fischer2a47dea2007-04-04 14:01:23 +0000304die_outfile:
Manuel Novoa III cad53642003-03-19 09:13:01 +0000305 bb_perror_msg_and_die("%s", outfile);
Glenn L McGratheaed78a2002-11-28 11:05:28 +0000306 }
Bernhard Reutner-Fischer2a47dea2007-04-04 14:01:23 +0000307out_status:
Rob Landleyc5598172006-05-02 22:44:04 +0000308 dd_output_status(0);
Matt Kraai24ac0172000-12-18 21:38:57 +0000309
Matt Kraai3e856ce2000-12-01 02:55:13 +0000310 return EXIT_SUCCESS;
Eric Andersen3cf52d11999-10-12 22:26:06 +0000311}