Commit | Line | Data |
---|---|---|
ecc449eb | 1 | /*- |
ba5e8546 KB |
2 | * Copyright (c) 1991, 1993 |
3 | * The Regents of the University of California. All rights reserved. | |
ecc449eb | 4 | * |
b14ecbb1 | 5 | * This code is derived from software contributed to Berkeley by |
a08a4cd0 KB |
6 | * Keith Muller of the University of California, San Diego and Lance |
7 | * Visser of Convex Computer Corporation. | |
b14ecbb1 KB |
8 | * |
9 | * %sccs.include.redist.c% | |
ecc449eb KB |
10 | */ |
11 | ||
12 | #ifndef lint | |
ba5e8546 KB |
13 | static char copyright[] = |
14 | "@(#) Copyright (c) 1991, 1993\n\ | |
15 | The Regents of the University of California. All rights reserved.\n"; | |
ecc449eb KB |
16 | #endif /* not lint */ |
17 | ||
794629bf | 18 | #ifndef lint |
08ba8862 | 19 | static char sccsid[] = "@(#)dd.c 8.3 (Berkeley) %G%"; |
ecc449eb | 20 | #endif /* not lint */ |
794629bf | 21 | |
b14ecbb1 KB |
22 | #include <sys/param.h> |
23 | #include <sys/stat.h> | |
4bd44556 MK |
24 | #include <sys/ioctl.h> |
25 | #include <sys/mtio.h> | |
0bd4ec56 KB |
26 | |
27 | #include <ctype.h> | |
08ba8862 | 28 | #include <err.h> |
b14ecbb1 | 29 | #include <errno.h> |
0bd4ec56 KB |
30 | #include <fcntl.h> |
31 | #include <signal.h> | |
b6b73269 | 32 | #include <stdio.h> |
2f339969 KB |
33 | #include <stdlib.h> |
34 | #include <string.h> | |
0bd4ec56 KB |
35 | #include <unistd.h> |
36 | ||
b14ecbb1 KB |
37 | #include "dd.h" |
38 | #include "extern.h" | |
ae2da14a | 39 | |
b14ecbb1 KB |
40 | static void dd_close __P((void)); |
41 | static void dd_in __P((void)); | |
42 | static void setup __P((void)); | |
2f339969 | 43 | |
998e1ac9 KB |
44 | IO in, out; /* input/output state */ |
45 | STAT st; /* statistics */ | |
0bd4ec56 | 46 | void (*cfunc) __P((void)); /* conversion function */ |
b14ecbb1 KB |
47 | u_long cpy_cnt; /* # of blocks to copy */ |
48 | u_int ddflags; /* conversion options */ | |
49 | u_int cbsz; /* conversion block size */ | |
50 | u_int files_cnt = 1; /* # of files to copy */ | |
51 | u_char *ctab; /* conversion table */ | |
ae2da14a | 52 | |
f290a0b2 | 53 | int |
b14ecbb1 KB |
54 | main(argc, argv) |
55 | int argc; | |
f290a0b2 | 56 | char *argv[]; |
ae2da14a | 57 | { |
b14ecbb1 KB |
58 | jcl(argv); |
59 | setup(); | |
ae2da14a | 60 | |
08ba8862 | 61 | (void)signal(SIGINFO, summaryx); |
b14ecbb1 | 62 | (void)signal(SIGINT, terminate); |
ae2da14a | 63 | |
08ba8862 JSP |
64 | atexit(summary); |
65 | ||
66 | while (files_cnt--) | |
b14ecbb1 | 67 | dd_in(); |
ae2da14a | 68 | |
b14ecbb1 | 69 | dd_close(); |
b14ecbb1 | 70 | exit(0); |
ae2da14a BJ |
71 | } |
72 | ||
b14ecbb1 | 73 | static void |
08ba8862 JSP |
74 | getfdtype(io) |
75 | IO *io; | |
ae2da14a | 76 | { |
b14ecbb1 | 77 | struct mtget mt; |
08ba8862 JSP |
78 | struct stat sb; |
79 | ||
80 | if (fstat(io->fd, &sb)) | |
81 | err(1, "%s", io->name); | |
82 | if (S_ISCHR(sb.st_mode)) | |
83 | io->flags |= ioctl(io->fd, MTIOCGET, &mt) ? ISCHR : ISTAPE; | |
84 | else if (lseek(io->fd, (off_t)0, SEEK_CUR) == -1 && errno == ESPIPE) | |
85 | io->flags |= ISPIPE; /* XXX fixed in 4.4BSD */ | |
86 | } | |
87 | ||
88 | static void | |
89 | setup() | |
90 | { | |
91 | u_int cnt; | |
ae2da14a | 92 | |
b14ecbb1 KB |
93 | if (in.name == NULL) { |
94 | in.name = "stdin"; | |
95 | in.fd = STDIN_FILENO; | |
96 | } else { | |
97 | in.fd = open(in.name, O_RDONLY, 0); | |
98 | if (in.fd < 0) | |
08ba8862 | 99 | err(1, "%s", in.name); |
ae2da14a | 100 | } |
ae2da14a | 101 | |
08ba8862 | 102 | getfdtype(&in); |
b14ecbb1 KB |
103 | |
104 | if (files_cnt > 1 && !(in.flags & ISTAPE)) | |
08ba8862 | 105 | errx(1, "files is not supported for non-tape devices"); |
b14ecbb1 KB |
106 | |
107 | if (out.name == NULL) { | |
108 | /* No way to check for read access here. */ | |
109 | out.fd = STDOUT_FILENO; | |
110 | out.name = "stdout"; | |
111 | } else { | |
7c547af1 KB |
112 | #define OFLAGS \ |
113 | (O_CREAT | (ddflags & (C_SEEK | C_NOTRUNC) ? 0 : O_TRUNC)) | |
114 | out.fd = open(out.name, O_RDWR | OFLAGS, DEFFILEMODE); | |
b14ecbb1 KB |
115 | /* |
116 | * May not have read access, so try again with write only. | |
117 | * Without read we may have a problem if output also does | |
118 | * not support seeks. | |
119 | */ | |
120 | if (out.fd < 0) { | |
7c547af1 | 121 | out.fd = open(out.name, O_WRONLY | OFLAGS, DEFFILEMODE); |
b14ecbb1 KB |
122 | out.flags |= NOREAD; |
123 | } | |
124 | if (out.fd < 0) | |
08ba8862 | 125 | err(1, "%s", out.name); |
ae2da14a | 126 | } |
ae2da14a | 127 | |
08ba8862 | 128 | getfdtype(&out); |
b14ecbb1 KB |
129 | |
130 | /* | |
131 | * Allocate space for the input and output buffers. If not doing | |
132 | * record oriented I/O, only need a single buffer. | |
133 | */ | |
134 | if (!(ddflags & (C_BLOCK|C_UNBLOCK))) { | |
0dcc0342 | 135 | if ((in.db = malloc(out.dbsz + in.dbsz - 1)) == NULL) |
08ba8862 | 136 | err(1, NULL); |
b14ecbb1 KB |
137 | out.db = in.db; |
138 | } else if ((in.db = | |
139 | malloc((u_int)(MAX(in.dbsz, cbsz) + cbsz))) == NULL || | |
140 | (out.db = malloc((u_int)(out.dbsz + cbsz))) == NULL) | |
08ba8862 | 141 | err(1, NULL); |
b14ecbb1 KB |
142 | in.dbp = in.db; |
143 | out.dbp = out.db; | |
144 | ||
145 | /* Position the input/output streams. */ | |
146 | if (in.offset) | |
147 | pos_in(); | |
148 | if (out.offset) | |
149 | pos_out(); | |
150 | ||
7c547af1 KB |
151 | /* |
152 | * Truncate the output file; ignore errors because it fails on some | |
153 | * kinds of output files, tapes, for example. | |
154 | */ | |
155 | if (ddflags & (C_OF | C_SEEK | C_NOTRUNC) == (C_OF | C_SEEK)) | |
156 | (void)ftruncate(out.fd, (off_t)out.offset * out.dbsz); | |
b14ecbb1 KB |
157 | |
158 | /* | |
159 | * If converting case at the same time as another conversion, build a | |
160 | * table that does both at once. If just converting case, use the | |
161 | * built-in tables. | |
162 | */ | |
163 | if (ddflags & (C_LCASE|C_UCASE)) | |
998e1ac9 | 164 | if (ddflags & C_ASCII) |
b14ecbb1 KB |
165 | if (ddflags & C_LCASE) { |
166 | for (cnt = 0; cnt < 0377; ++cnt) | |
998e1ac9 KB |
167 | if (isupper(ctab[cnt])) |
168 | ctab[cnt] = tolower(ctab[cnt]); | |
b14ecbb1 KB |
169 | } else { |
170 | for (cnt = 0; cnt < 0377; ++cnt) | |
998e1ac9 KB |
171 | if (islower(ctab[cnt])) |
172 | ctab[cnt] = toupper(ctab[cnt]); | |
b14ecbb1 | 173 | } |
998e1ac9 | 174 | else if (ddflags & C_EBCDIC) |
b14ecbb1 KB |
175 | if (ddflags & C_LCASE) { |
176 | for (cnt = 0; cnt < 0377; ++cnt) | |
998e1ac9 KB |
177 | if (isupper(cnt)) |
178 | ctab[cnt] = ctab[tolower(cnt)]; | |
b14ecbb1 KB |
179 | } else { |
180 | for (cnt = 0; cnt < 0377; ++cnt) | |
998e1ac9 KB |
181 | if (islower(cnt)) |
182 | ctab[cnt] = ctab[toupper(cnt)]; | |
b14ecbb1 | 183 | } |
998e1ac9 | 184 | else |
b14ecbb1 | 185 | ctab = ddflags & C_LCASE ? u2l : l2u; |
5c810d41 | 186 | (void)time(&st.start); /* Statistics timestamp. */ |
ae2da14a BJ |
187 | } |
188 | ||
b14ecbb1 KB |
189 | static void |
190 | dd_in() | |
ae2da14a | 191 | { |
08ba8862 | 192 | int flags, n; |
b14ecbb1 | 193 | |
122b4d24 | 194 | for (flags = ddflags;;) { |
998e1ac9 | 195 | if (cpy_cnt && (st.in_full + st.in_part) >= cpy_cnt) |
b14ecbb1 KB |
196 | return; |
197 | ||
198 | /* | |
199 | * Zero the buffer first if trying to recover from errors so | |
200 | * lose the minimum amount of data. If doing block operations | |
201 | * use spaces. | |
202 | */ | |
a8c9b9fe | 203 | if ((flags & (C_NOERROR|C_SYNC)) == (C_NOERROR|C_SYNC)) |
b14ecbb1 KB |
204 | if (flags & (C_BLOCK|C_UNBLOCK)) |
205 | memset(in.dbp, ' ', in.dbsz); | |
206 | else | |
0bd4ec56 | 207 | memset(in.dbp, 0, in.dbsz); |
b14ecbb1 KB |
208 | |
209 | n = read(in.fd, in.dbp, in.dbsz); | |
210 | if (n == 0) { | |
211 | in.dbrcnt = 0; | |
212 | return; | |
213 | } | |
ae2da14a | 214 | |
b14ecbb1 KB |
215 | /* Read error. */ |
216 | if (n < 0) { | |
217 | /* | |
218 | * If noerror not specified, die. POSIX requires that | |
219 | * the warning message be followed by an I/O display. | |
220 | */ | |
221 | if (!(flags & C_NOERROR)) | |
08ba8862 JSP |
222 | err(1, "%s", in.name); |
223 | warn("%s", in.name); | |
224 | summary(); | |
b14ecbb1 KB |
225 | |
226 | /* | |
227 | * If it's not a tape drive or a pipe, seek past the | |
228 | * error. If your OS doesn't do the right thing for | |
229 | * raw disks this section should be modified to re-read | |
230 | * in sector size chunks. | |
231 | */ | |
232 | if (!(in.flags & (ISPIPE|ISTAPE)) && | |
233 | lseek(in.fd, (off_t)in.dbsz, SEEK_CUR)) | |
08ba8862 | 234 | warn("%s", in.name); |
b14ecbb1 KB |
235 | |
236 | /* If sync not specified, omit block and continue. */ | |
237 | if (!(ddflags & C_SYNC)) | |
238 | continue; | |
ae2da14a | 239 | |
b14ecbb1 KB |
240 | /* Read errors count as full blocks. */ |
241 | in.dbcnt += in.dbrcnt = in.dbsz; | |
998e1ac9 | 242 | ++st.in_full; |
b14ecbb1 KB |
243 | |
244 | /* Handle full input blocks. */ | |
245 | } else if (n == in.dbsz) { | |
246 | in.dbcnt += in.dbrcnt = n; | |
998e1ac9 | 247 | ++st.in_full; |
b14ecbb1 KB |
248 | |
249 | /* Handle partial input blocks. */ | |
122b4d24 | 250 | } else { |
a7db955e KB |
251 | /* If sync, use the entire block. */ |
252 | if (ddflags & C_SYNC) | |
b14ecbb1 | 253 | in.dbcnt += in.dbrcnt = in.dbsz; |
a7db955e | 254 | else |
b14ecbb1 | 255 | in.dbcnt += in.dbrcnt = n; |
998e1ac9 | 256 | ++st.in_part; |
ae2da14a | 257 | } |
ae2da14a | 258 | |
b14ecbb1 KB |
259 | /* |
260 | * POSIX states that if bs is set and no other conversions | |
acb87732 KB |
261 | * than noerror, notrunc or sync are specified, the block |
262 | * is output without buffering as it is read. | |
b14ecbb1 KB |
263 | */ |
264 | if (ddflags & C_BS) { | |
265 | out.dbcnt = in.dbcnt; | |
266 | dd_out(1); | |
267 | in.dbcnt = 0; | |
268 | continue; | |
ae2da14a | 269 | } |
ae2da14a | 270 | |
acb87732 KB |
271 | if (ddflags & C_SWAB) { |
272 | if ((n = in.dbcnt) & 1) { | |
273 | ++st.swab; | |
274 | --n; | |
275 | } | |
276 | swab(in.dbp, in.dbp, n); | |
277 | } | |
278 | ||
b14ecbb1 KB |
279 | in.dbp += in.dbrcnt; |
280 | (*cfunc)(); | |
ae2da14a | 281 | } |
ae2da14a BJ |
282 | } |
283 | ||
b14ecbb1 KB |
284 | /* |
285 | * Cleanup any remaining I/O and flush output. If necesssary, output file | |
286 | * is truncated. | |
287 | */ | |
288 | static void | |
289 | dd_close() | |
ae2da14a | 290 | { |
b14ecbb1 KB |
291 | if (cfunc == def) |
292 | def_close(); | |
293 | else if (cfunc == block) | |
294 | block_close(); | |
295 | else if (cfunc == unblock) | |
296 | unblock_close(); | |
a8c9b9fe | 297 | if (ddflags & C_OSYNC && out.dbcnt < out.dbsz) { |
08ba8862 | 298 | memset(out.dbp, 0, out.dbsz - out.dbcnt); |
a8c9b9fe KB |
299 | out.dbcnt = out.dbsz; |
300 | } | |
b14ecbb1 KB |
301 | if (out.dbcnt) |
302 | dd_out(1); | |
ae2da14a BJ |
303 | } |
304 | ||
2f339969 | 305 | void |
b14ecbb1 KB |
306 | dd_out(force) |
307 | int force; | |
ae2da14a | 308 | { |
b14ecbb1 | 309 | static int warned; |
08ba8862 JSP |
310 | int cnt, n, nw; |
311 | u_char *outp; | |
b14ecbb1 KB |
312 | |
313 | /* | |
314 | * Write one or more blocks out. The common case is writing a full | |
315 | * output block in a single write; increment the full block stats. | |
316 | * Otherwise, we're into partial block writes. If a partial write, | |
317 | * and it's a character device, just warn. If a tape device, quit. | |
318 | * | |
319 | * The partial writes represent two cases. 1: Where the input block | |
320 | * was less than expected so the output block was less than expected. | |
321 | * 2: Where the input block was the right size but we were forced to | |
322 | * write the block in multiple chunks. The original versions of dd(1) | |
323 | * never wrote a block in more than a single write, so the latter case | |
324 | * never happened. | |
325 | * | |
326 | * One special case is if we're forced to do the write -- in that case | |
327 | * we play games with the buffer size, and it's usually a partial write. | |
328 | */ | |
329 | outp = out.db; | |
330 | for (n = force ? out.dbcnt : out.dbsz;; n = out.dbsz) { | |
331 | for (cnt = n;; cnt -= nw) { | |
122b4d24 | 332 | nw = write(out.fd, outp, cnt); |
59cab7ca KB |
333 | if (nw <= 0) { |
334 | if (nw == 0) | |
08ba8862 | 335 | errx(1, "%s: end of device", out.name); |
59cab7ca | 336 | if (errno != EINTR) |
08ba8862 | 337 | err(1, "%s", out.name); |
59cab7ca KB |
338 | nw = 0; |
339 | } | |
2edc4512 | 340 | outp += nw; |
5c810d41 | 341 | st.bytes += nw; |
b14ecbb1 KB |
342 | if (nw == n) { |
343 | if (n != out.dbsz) | |
998e1ac9 | 344 | ++st.out_part; |
b14ecbb1 | 345 | else |
998e1ac9 | 346 | ++st.out_full; |
b14ecbb1 KB |
347 | break; |
348 | } | |
998e1ac9 | 349 | ++st.out_part; |
b14ecbb1 KB |
350 | if (nw == cnt) |
351 | break; | |
352 | if (out.flags & ISCHR && !warned) { | |
353 | warned = 1; | |
08ba8862 | 354 | warnx("%s: short write on character device", |
b14ecbb1 KB |
355 | out.name); |
356 | } | |
357 | if (out.flags & ISTAPE) | |
08ba8862 | 358 | errx(1, "%s: short write on tape device", out.name); |
b14ecbb1 KB |
359 | } |
360 | if ((out.dbcnt -= n) < out.dbsz) | |
361 | break; | |
4bd44556 | 362 | } |
4bd44556 | 363 | |
b14ecbb1 KB |
364 | /* Reassemble the output block. */ |
365 | if (out.dbcnt) | |
0bd4ec56 | 366 | memmove(out.db, out.dbp - out.dbcnt, out.dbcnt); |
b14ecbb1 | 367 | out.dbp = out.db + out.dbcnt; |
4bd44556 | 368 | } |