Commit | Line | Data |
---|---|---|
ecc449eb KB |
1 | /*- |
2 | * Copyright (c) 1991 The Regents of the University of California. | |
3 | * All rights reserved. | |
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 | |
13 | char copyright[] = | |
14 | "@(#) Copyright (c) 1991 The Regents of the University of California.\n\ | |
15 | All rights reserved.\n"; | |
16 | #endif /* not lint */ | |
17 | ||
794629bf | 18 | #ifndef lint |
acb87732 | 19 | static char sccsid[] = "@(#)dd.c 5.9 (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> | |
ae2da14a | 26 | #include <signal.h> |
b6b73269 KB |
27 | #include <fcntl.h> |
28 | #include <unistd.h> | |
b14ecbb1 | 29 | #include <errno.h> |
b6b73269 | 30 | #include <stdio.h> |
b14ecbb1 | 31 | #include <ctype.h> |
2f339969 KB |
32 | #include <stdlib.h> |
33 | #include <string.h> | |
b14ecbb1 KB |
34 | #include "dd.h" |
35 | #include "extern.h" | |
ae2da14a | 36 | |
b14ecbb1 KB |
37 | static void dd_close __P((void)); |
38 | static void dd_in __P((void)); | |
39 | static void setup __P((void)); | |
2f339969 | 40 | |
998e1ac9 KB |
41 | IO in, out; /* input/output state */ |
42 | STAT st; /* statistics */ | |
b14ecbb1 KB |
43 | void (*cfunc)(); /* conversion function */ |
44 | u_long cpy_cnt; /* # of blocks to copy */ | |
45 | u_int ddflags; /* conversion options */ | |
46 | u_int cbsz; /* conversion block size */ | |
47 | u_int files_cnt = 1; /* # of files to copy */ | |
48 | u_char *ctab; /* conversion table */ | |
ae2da14a | 49 | |
b14ecbb1 KB |
50 | main(argc, argv) |
51 | int argc; | |
52 | char **argv; | |
ae2da14a | 53 | { |
b14ecbb1 KB |
54 | jcl(argv); |
55 | setup(); | |
ae2da14a | 56 | |
b14ecbb1 KB |
57 | (void)signal(SIGINFO, summary); |
58 | (void)signal(SIGINT, terminate); | |
ae2da14a | 59 | |
b14ecbb1 KB |
60 | while (files_cnt--) |
61 | dd_in(); | |
ae2da14a | 62 | |
b14ecbb1 KB |
63 | dd_close(); |
64 | summary(0); | |
65 | exit(0); | |
ae2da14a BJ |
66 | } |
67 | ||
b14ecbb1 KB |
68 | static void |
69 | setup() | |
ae2da14a | 70 | { |
b14ecbb1 KB |
71 | register u_int cnt; |
72 | struct stat sb; | |
73 | struct mtget mt; | |
ae2da14a | 74 | |
b14ecbb1 KB |
75 | if (in.name == NULL) { |
76 | in.name = "stdin"; | |
77 | in.fd = STDIN_FILENO; | |
78 | } else { | |
79 | in.fd = open(in.name, O_RDONLY, 0); | |
80 | if (in.fd < 0) | |
81 | err("%s: %s", in.name, strerror(errno)); | |
ae2da14a | 82 | } |
ae2da14a | 83 | |
b14ecbb1 KB |
84 | if (fstat(in.fd, &sb)) |
85 | err("%s: %s", in.name, strerror(errno)); | |
86 | if (S_ISCHR(sb.st_mode)) | |
87 | in.flags |= ioctl(in.fd, MTIOCGET, &mt) ? ISCHR : ISTAPE; | |
88 | else if (lseek(in.fd, 0L, SEEK_CUR) == -1 && errno == ESPIPE) | |
89 | in.flags |= ISPIPE; /* XXX fixed in 4.4BSD */ | |
90 | ||
91 | if (files_cnt > 1 && !(in.flags & ISTAPE)) | |
92 | err("files is not supported for non-tape devices"); | |
93 | ||
94 | if (out.name == NULL) { | |
95 | /* No way to check for read access here. */ | |
96 | out.fd = STDOUT_FILENO; | |
97 | out.name = "stdout"; | |
98 | } else { | |
99 | out.fd = open(out.name, O_RDWR|O_CREAT, DEFFILEMODE); | |
100 | /* | |
101 | * May not have read access, so try again with write only. | |
102 | * Without read we may have a problem if output also does | |
103 | * not support seeks. | |
104 | */ | |
105 | if (out.fd < 0) { | |
106 | out.fd = open(out.name, O_WRONLY|O_CREAT, DEFFILEMODE); | |
107 | out.flags |= NOREAD; | |
108 | } | |
109 | if (out.fd < 0) | |
110 | err("%s: %s", out.name, strerror(errno)); | |
ae2da14a | 111 | } |
ae2da14a | 112 | |
b14ecbb1 KB |
113 | if (fstat(out.fd, &sb)) |
114 | err("%s: %s", out.name, strerror(errno)); | |
115 | if (S_ISCHR(sb.st_mode)) | |
116 | out.flags |= ioctl(out.fd, MTIOCGET, &mt) ? ISCHR : ISTAPE; | |
117 | else if (lseek(out.fd, 0L, SEEK_CUR) == -1 && errno == ESPIPE) | |
118 | out.flags |= ISPIPE; /* XXX fixed in 4.4BSD */ | |
119 | ||
120 | /* | |
121 | * Allocate space for the input and output buffers. If not doing | |
122 | * record oriented I/O, only need a single buffer. | |
123 | */ | |
124 | if (!(ddflags & (C_BLOCK|C_UNBLOCK))) { | |
125 | if (in.dbsz > out.dbsz) | |
126 | cnt = in.dbsz - 1 - in.dbsz / 2 + in.dbsz; | |
127 | else if (in.dbsz < out.dbsz) | |
128 | cnt = out.dbsz + in.dbsz - 1; | |
129 | else | |
130 | cnt = in.dbsz; | |
131 | if ((in.db = malloc(cnt)) == NULL) | |
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 | ||
147 | /* Truncate the output file. */ | |
148 | if (ddflags & C_NOTRUNC) { | |
149 | if (out.flags & ISTAPE) | |
150 | err("notrunc is not supported for tape devices"); | |
151 | } else if (S_ISREG(sb.st_mode) && ftruncate(out.fd, | |
152 | (off_t)out.offset * out.dbsz)) | |
153 | err("%s: truncate: %s", out.name, strerror(errno)); | |
154 | ||
155 | /* | |
156 | * If converting case at the same time as another conversion, build a | |
157 | * table that does both at once. If just converting case, use the | |
158 | * built-in tables. | |
159 | */ | |
160 | if (ddflags & (C_LCASE|C_UCASE)) | |
998e1ac9 | 161 | if (ddflags & C_ASCII) |
b14ecbb1 KB |
162 | if (ddflags & C_LCASE) { |
163 | for (cnt = 0; cnt < 0377; ++cnt) | |
998e1ac9 KB |
164 | if (isupper(ctab[cnt])) |
165 | ctab[cnt] = tolower(ctab[cnt]); | |
b14ecbb1 KB |
166 | } else { |
167 | for (cnt = 0; cnt < 0377; ++cnt) | |
998e1ac9 KB |
168 | if (islower(ctab[cnt])) |
169 | ctab[cnt] = toupper(ctab[cnt]); | |
b14ecbb1 | 170 | } |
998e1ac9 | 171 | else if (ddflags & C_EBCDIC) |
b14ecbb1 KB |
172 | if (ddflags & C_LCASE) { |
173 | for (cnt = 0; cnt < 0377; ++cnt) | |
998e1ac9 KB |
174 | if (isupper(cnt)) |
175 | ctab[cnt] = ctab[tolower(cnt)]; | |
b14ecbb1 KB |
176 | } else { |
177 | for (cnt = 0; cnt < 0377; ++cnt) | |
998e1ac9 KB |
178 | if (islower(cnt)) |
179 | ctab[cnt] = ctab[toupper(cnt)]; | |
b14ecbb1 | 180 | } |
998e1ac9 | 181 | else |
b14ecbb1 | 182 | ctab = ddflags & C_LCASE ? u2l : l2u; |
ae2da14a BJ |
183 | } |
184 | ||
b14ecbb1 KB |
185 | static void |
186 | dd_in() | |
ae2da14a | 187 | { |
b14ecbb1 KB |
188 | register int flags, n; |
189 | ||
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 | |
203 | bzero(in.dbp, in.dbsz); | |
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. */ | |
246 | } else if (n != in.dbsz) { | |
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) { | |
324 | outp += nw = write(out.fd, outp, cnt); | |
325 | if (nw == n) { | |
326 | if (n != out.dbsz) | |
998e1ac9 | 327 | ++st.out_part; |
b14ecbb1 | 328 | else |
998e1ac9 | 329 | ++st.out_full; |
b14ecbb1 KB |
330 | break; |
331 | } | |
332 | if (nw < 0) | |
333 | err("%s: %s", out.name, strerror(errno)); | |
998e1ac9 | 334 | ++st.out_part; |
b14ecbb1 KB |
335 | if (nw == cnt) |
336 | break; | |
337 | if (out.flags & ISCHR && !warned) { | |
338 | warned = 1; | |
339 | warn("%s: short write on character device", | |
340 | out.name); | |
341 | } | |
342 | if (out.flags & ISTAPE) | |
343 | err("%s: short write on tape device", out.name); | |
344 | } | |
345 | if ((out.dbcnt -= n) < out.dbsz) | |
346 | break; | |
4bd44556 | 347 | } |
4bd44556 | 348 | |
b14ecbb1 KB |
349 | /* Reassemble the output block. */ |
350 | if (out.dbcnt) | |
351 | bcopy(out.dbp - out.dbcnt, out.db, out.dbcnt); | |
352 | out.dbp = out.db + out.dbcnt; | |
4bd44556 | 353 | } |