33e789311071c53ebb921605c409ccc9642cc65e
[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 <sys/types.h>
12 #include <sys/stat.h>
13 #include <stdlib.h>
14 #include <stdio.h>
15 #include <unistd.h>
16 #include <string.h>
17 #include <fcntl.h>
18 #include <signal.h>  // For FEATURE_DD_SIGNAL_HANDLING
19 #include "busybox.h"
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         { "MD", 1000000 },
28         { "M", 1048576 },
29         { "GD", 1000000000 },
30         { "G", 1073741824 },
31         { NULL, 0 }
32 };
33
34 static size_t out_full;
35 static size_t out_part;
36 static size_t in_full;
37 static size_t in_part;
38
39 static void dd_output_status(int cur_signal)
40 {
41         fprintf(stderr, "%ld+%ld records in\n%ld+%ld records out\n",
42                         (long)in_full, (long)in_part,
43                         (long)out_full, (long)out_part);
44 }
45
46 int dd_main(int argc, char **argv)
47 {
48         size_t count = -1, oc = 0, ibs = 512, obs = 512;
49         ssize_t n;
50         off_t seek = 0, skip = 0;
51         int sync_flag = FALSE, noerror = FALSE, trunc_flag = TRUE, twobufs_flag = 0,
52                 oflag, ifd, ofd, i;
53         const char *infile = NULL, *outfile = NULL;
54         char *ibuf, *obuf;
55
56         if (ENABLE_FEATURE_DD_SIGNAL_HANDLING)
57         {
58                 struct sigaction sa;
59
60                 memset(&sa, 0, sizeof(sa));
61                 sa.sa_handler = dd_output_status; 
62                 sa.sa_flags = SA_RESTART;
63                 sigemptyset(&sa.sa_mask);
64                 sigaction(SIGUSR1, &sa, 0); 
65         }
66
67         for (i = 1; i < argc; i++) {
68                 if (ENABLE_FEATURE_DD_IBS_OBS && !strncmp("ibs=", argv[i], 4)) {
69                         ibs = bb_xparse_number(argv[i]+4, dd_suffixes);
70                         twobufs_flag++;
71                 } else if (ENABLE_FEATURE_DD_IBS_OBS && !strncmp("obs=", argv[i], 4)) {
72                         obs = bb_xparse_number(argv[i]+4, dd_suffixes);
73                         twobufs_flag++;
74                 } else if (!strncmp("bs=", argv[i], 3)) {
75                         ibs = obs = bb_xparse_number(argv[i]+3, dd_suffixes);
76                 } else if (!strncmp("count=", argv[i], 6))
77                         count = bb_xparse_number(argv[i]+6, dd_suffixes);
78                 else if (!strncmp("seek=", argv[i], 5))
79                         seek = bb_xparse_number(argv[i]+5, dd_suffixes);
80                 else if (!strncmp("skip=", argv[i], 5))
81                         skip = bb_xparse_number(argv[i]+5, dd_suffixes);
82                 else if (!strncmp("if=", argv[i], 3))
83                         infile = argv[i]+3;
84                 else if (!strncmp("of=", argv[i], 3))
85                         outfile = argv[i]+3;
86                 else if (ENABLE_FEATURE_DD_IBS_OBS && !strncmp("conv=", argv[i], 5)) {
87                         ibuf = argv[i]+5;
88                         while (1) {
89                                 if (!strncmp("notrunc", ibuf, 7)) {
90                                         trunc_flag = FALSE;
91                                         ibuf += 7;
92                                 } else if (!strncmp("sync", ibuf, 4)) {
93                                         sync_flag = TRUE;
94                                         ibuf += 4;
95                                 } else if (!strncmp("noerror", ibuf, 7)) {
96                                         noerror = TRUE;
97                                         ibuf += 7;
98                                 } else {
99                                         bb_error_msg_and_die(bb_msg_invalid_arg, argv[i]+5, "conv");
100                                 }
101                                 if (ibuf[0] == '\0') break;
102                                 if (ibuf[0] == ',') ibuf++;
103                         }
104                 } else
105                         bb_show_usage();
106         }
107         ibuf = xmalloc(ibs);
108
109         if (twobufs_flag) obuf = xmalloc(obs);
110         else obuf = ibuf;
111
112         if (infile != NULL) {
113                 ifd = bb_xopen(infile, O_RDONLY);
114         } else {
115                 ifd = STDIN_FILENO;
116                 infile = bb_msg_standard_input;
117         }
118
119         if (outfile != NULL) {
120                 oflag = O_WRONLY | O_CREAT;
121
122                 if (!seek && trunc_flag) {
123                         oflag |= O_TRUNC;
124                 }
125
126                 ofd = bb_xopen3(outfile, oflag, 0666);
127
128                 if (seek && trunc_flag) {
129                         if (ftruncate(ofd, seek * obs) < 0) {
130                                 struct stat st;
131
132                                 if (fstat (ofd, &st) < 0 || S_ISREG (st.st_mode) ||
133                                                 S_ISDIR (st.st_mode)) {
134                                         bb_perror_msg_and_die("%s", outfile);
135                                 }
136                         }
137                 }
138         } else {
139                 ofd = STDOUT_FILENO;
140                 outfile = bb_msg_standard_output;
141         }
142
143         if (skip) {
144                 if (lseek(ifd, skip * ibs, SEEK_CUR) < 0) {
145                         while (skip-- > 0) {
146                                 n = safe_read(ifd, ibuf, ibs);
147                                 if (n < 0)
148                                         bb_perror_msg_and_die("%s", infile);
149                                 if (n == 0)
150                                         break;
151                         }
152                 }
153         }
154
155         if (seek) {
156                 if (lseek(ofd, seek * obs, SEEK_CUR) < 0) {
157                         bb_perror_msg_and_die("%s", outfile);
158                 }
159         }
160
161         while (in_full + in_part != count) {
162                 if (noerror) {
163                         /* Pre-zero the buffer when doing the noerror thing */
164                         memset(ibuf, '\0', ibs);
165                 }
166
167                 n = safe_read(ifd, ibuf, ibs);
168                 if (n == 0) {
169                         break;
170                 }
171                 if (n < 0) {
172                         if (noerror) {
173                                 n = ibs;
174                                 bb_perror_msg("%s", infile);
175                         } else {
176                                 bb_perror_msg_and_die("%s", infile);
177                         }
178                 }
179                 if ((size_t)n == ibs) {
180                         in_full++;
181                 } else {
182                         in_part++;
183                         if (sync_flag) {
184                                 memset(ibuf + n, '\0', ibs - n);
185                                 n = ibs;
186                         }
187                 }
188                 if (twobufs_flag) {
189                         char *tmp = ibuf;
190                         while (n) {
191                                 size_t d = obs - oc;
192
193                                 if (d > n) d = n;
194                                 memcpy(obuf + oc, tmp, d);
195                                 n -= d;
196                                 tmp += d;
197                                 oc += d;
198                                 if (oc == obs) {
199                                         if (bb_full_write(ofd, obuf, obs) < 0) {
200                                                 bb_perror_msg_and_die("%s", outfile);
201                                         }
202                                         out_full++;
203                                         oc = 0;
204                                 }
205                         }
206                 } else {
207                         if ((n = bb_full_write(ofd, ibuf, n)) < 0) {
208                                 bb_perror_msg_and_die("%s", outfile);
209                         }
210                         if (n == ibs) out_full++;
211                         else out_part++;
212                 }
213         }
214         
215         if (ENABLE_FEATURE_DD_IBS_OBS && oc) {
216                 if (bb_full_write(ofd, obuf, oc) < 0) {
217                         bb_perror_msg_and_die("%s", outfile);
218                 }
219                 out_part++;
220         }
221         if (close (ifd) < 0) {
222                 bb_perror_msg_and_die("%s", infile);
223         }
224
225         if (close (ofd) < 0) {
226                 bb_perror_msg_and_die("%s", outfile);
227         }
228
229         dd_output_status(0);
230
231         return EXIT_SUCCESS;
232 }