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
17static 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
31struct 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
39static void dd_output_status(int ATTRIBUTE_UNUSED cur_signal)
40{
41	/* Deliberately using %u, not %d */
42	fprintf(stderr, "%"OFF_FMT"u+%"OFF_FMT"u records in\n"
43			"%"OFF_FMT"u+%"OFF_FMT"u records out\n",
44			G.in_full, G.in_part,
45			G.out_full, G.out_part);
46}
47
48static ssize_t full_write_or_warn(int fd, const void *buf, size_t len,
49	const char *const filename)
50{
51	ssize_t n = full_write(fd, buf, len);
52	if (n < 0)
53		bb_perror_msg("writing '%s'", filename);
54	return n;
55}
56
57static bool write_and_stats(int fd, const void *buf, size_t len, size_t obs,
58	const char *filename)
59{
60	ssize_t n = full_write_or_warn(fd, buf, len, filename);
61	if (n < 0)
62		return 1;
63	if (n == obs)
64		G.out_full++;
65	else if (n) /* > 0 */
66		G.out_part++;
67	return 0;
68}
69
70#if ENABLE_LFS
71#define XATOU_SFX xatoull_sfx
72#else
73#define XATOU_SFX xatoul_sfx
74#endif
75
76int dd_main(int argc, char **argv);
77int dd_main(int argc, char **argv)
78{
79	enum {
80		FLAG_SYNC    = 1 << 0,
81		FLAG_NOERROR = 1 << 1,
82		FLAG_NOTRUNC = 1 << 2,
83		FLAG_TWOBUFS = 1 << 3,
84		FLAG_COUNT   = 1 << 4,
85	};
86	static const char keywords[] ALIGN1 =
87		"bs=\0""count=\0""seek=\0""skip=\0""if=\0""of=\0"
88#if ENABLE_FEATURE_DD_IBS_OBS
89		"ibs=\0""obs=\0""conv=\0""notrunc\0""sync\0""noerror\0"
90#endif
91		;
92	enum {
93		OP_bs = 1,
94		OP_count,
95		OP_seek,
96		OP_skip,
97		OP_if,
98		OP_of,
99#if ENABLE_FEATURE_DD_IBS_OBS
100		OP_ibs,
101		OP_obs,
102		OP_conv,
103		OP_conv_notrunc,
104		OP_conv_sync,
105		OP_conv_noerror,
106#endif
107	};
108	size_t ibs = 512, obs = 512;
109	ssize_t n, w;
110	char *ibuf, *obuf;
111	/* And these are all zeroed at once! */
112	struct {
113		int flags;
114		int ifd, ofd;
115		size_t oc;
116		off_t count;
117		off_t seek, skip;
118		const char *infile, *outfile;
119#if ENABLE_FEATURE_DD_SIGNAL_HANDLING
120		struct sigaction sigact;
121#endif
122	} Z;
123#define flags   (Z.flags  )
124#define ifd     (Z.ifd    )
125#define ofd     (Z.ofd    )
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#define sigact  (Z.sigact )
133
134	memset(&Z, 0, sizeof(Z));
135	INIT_G();
136
137#if ENABLE_FEATURE_DD_SIGNAL_HANDLING
138	sigact.sa_handler = dd_output_status;
139	sigact.sa_flags = SA_RESTART;
140	sigemptyset(&sigact.sa_mask);
141	sigaction(SIGUSR1, &sigact, NULL);
142#endif
143
144	for (n = 1; n < argc; n++) {
145		smalluint key_len;
146		smalluint what;
147		char *key;
148		char *arg = argv[n];
149
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	ibuf = obuf = xmalloc(ibs);
223	if (ibs != obs) {
224		flags |= FLAG_TWOBUFS;
225		obuf = xmalloc(obs);
226	}
227	if (infile != NULL)
228		ifd = xopen(infile, O_RDONLY);
229	else {
230		/* ifd = STDIN_FILENO; - it's zero and it's already there */
231		infile = bb_msg_standard_input;
232	}
233	if (outfile != NULL) {
234		int oflag = O_WRONLY | O_CREAT;
235
236		if (!seek && !(flags & FLAG_NOTRUNC))
237			oflag |= O_TRUNC;
238
239		ofd = xopen(outfile, oflag);
240
241		if (seek && !(flags & FLAG_NOTRUNC)) {
242			if (ftruncate(ofd, seek * obs) < 0) {
243				struct stat st;
244
245				if (fstat(ofd, &st) < 0 || S_ISREG(st.st_mode) ||
246						S_ISDIR(st.st_mode))
247					goto die_outfile;
248			}
249		}
250	} else {
251		ofd = STDOUT_FILENO;
252		outfile = bb_msg_standard_output;
253	}
254	if (skip) {
255		if (lseek(ifd, skip * ibs, SEEK_CUR) < 0) {
256			while (skip-- > 0) {
257				n = safe_read(ifd, ibuf, ibs);
258				if (n < 0)
259					goto die_infile;
260				if (n == 0)
261					break;
262			}
263		}
264	}
265	if (seek) {
266		if (lseek(ofd, seek * obs, SEEK_CUR) < 0)
267			goto die_outfile;
268	}
269
270	while (!(flags & FLAG_COUNT) || (G.in_full + G.in_part != count)) {
271		if (flags & FLAG_NOERROR) /* Pre-zero the buffer if conv=noerror */
272			memset(ibuf, 0, ibs);
273		n = safe_read(ifd, ibuf, ibs);
274		if (n == 0)
275			break;
276		if (n < 0) {
277			if (flags & FLAG_NOERROR) {
278				n = ibs;
279				bb_perror_msg("%s", infile);
280			} else
281				goto die_infile;
282		}
283		if ((size_t)n == ibs)
284			G.in_full++;
285		else {
286			G.in_part++;
287			if (flags & FLAG_SYNC) {
288				memset(ibuf + n, '\0', ibs - n);
289				n = ibs;
290			}
291		}
292		if (flags & FLAG_TWOBUFS) {
293			char *tmp = ibuf;
294			while (n) {
295				size_t d = obs - oc;
296
297				if (d > n)
298					d = n;
299				memcpy(obuf + oc, tmp, d);
300				n -= d;
301				tmp += d;
302				oc += d;
303				if (oc == obs) {
304					if (write_and_stats(ofd, obuf, obs, obs, outfile))
305						goto out_status;
306					oc = 0;
307				}
308			}
309		} else if (write_and_stats(ofd, ibuf, n, obs, outfile))
310			goto out_status;
311	}
312
313	if (ENABLE_FEATURE_DD_IBS_OBS && oc) {
314		w = full_write_or_warn(ofd, obuf, oc, outfile);
315		if (w < 0) goto out_status;
316		if (w > 0)
317			G.out_part++;
318	}
319	if (close(ifd) < 0) {
320 die_infile:
321		bb_perror_msg_and_die("%s", infile);
322	}
323
324	if (close(ofd) < 0) {
325 die_outfile:
326		bb_perror_msg_and_die("%s", outfile);
327	}
328 out_status:
329	dd_output_status(0);
330
331	return EXIT_SUCCESS;
332}
333