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