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