1 /*
2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
5 * 1.0 of the CDDL.
6 *
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
10 */
11
12 /*
13 * Copyright (c) 2018 by Delphix. All rights reserved.
14 */
15
16 #include <sys/types.h>
17 #include <errno.h>
18 #include <fcntl.h>
19 #include <stdio.h>
20 #include <unistd.h>
21 #include <stdlib.h>
22 #include <string.h>
23
24 static int alignment = 0;
25 static int bsize = 0;
26 static int count = 0;
27 static char *ifile = NULL;
28 static char *ofile = NULL;
29 static off_t stride = 1;
30 static off_t seek = 0;
31 static int seekbytes = 0;
32 static int if_o_direct = 0;
33 static int of_o_direct = 0;
34 static int skip = 0;
35 static int skipbytes = 0;
36 static int entire_file = 0;
37 static const char *execname = "stride_dd";
38
39 static void usage(void);
40 static void parse_options(int argc, char *argv[]);
41
42 static void
usage(void)43 usage(void)
44 {
45 (void) fprintf(stderr,
46 "usage: %s -i inputfile -o outputfile -b blocksize [-c count]\n"
47 " [-s stride] [-k seekblocks] [-K seekbytes]\n"
48 " [-a alignment] [-d if_o_direct] [-D of_o_direct]\n"
49 " [-p skipblocks] [-P skipbytes] [-e entire_file]\n"
50 "\n"
51 "Simplified version of dd that supports the stride option.\n"
52 "A stride of n means that for each block written, n - 1 blocks\n"
53 "are skipped in both the input and output file. A stride of 1\n"
54 "means that blocks are read and written consecutively.\n"
55 "All numeric parameters must be integers.\n"
56 "\n"
57 " inputfile: File to read from\n"
58 " outputfile: File to write to\n"
59 " blocksize: Size of each block to read/write\n"
60 " count: Number of blocks to read/write (Required"
61 " unless -e is used)\n"
62 " stride: Read/write a block then skip (stride - 1) blocks"
63 "\n"
64 " seekblocks: Number of blocks to skip at start of output\n"
65 " seekbytes: Treat seekblocks as byte count\n"
66 " alignment: Alignment passed to posix_memalign() (default"
67 " PAGE_SIZE)\n"
68 " if_o_direct: Use O_DIRECT with inputfile (default no O_DIRECT)"
69 "\n"
70 " of_o_direct: Use O_DIRECT with outputfile (default no "
71 " O_DIRECT)\n"
72 " skipblocks: Number of blocks to skip at start of input "
73 " (default 0)\n"
74 " skipbytes: Treat skipblocks as byte count\n"
75 " entire_file: When used the entire inputfile will be read and"
76 " count will be ignored\n",
77 execname);
78 (void) exit(1);
79 }
80
81 /*
82 * posix_memalign() only allows for alignments which are postive, powers of two
83 * and a multiple of sizeof (void *).
84 */
85 static int
invalid_alignment(int alignment)86 invalid_alignment(int alignment)
87 {
88 if ((alignment < 0) || (alignment & (alignment - 1)) ||
89 ((alignment % sizeof (void *)))) {
90 (void) fprintf(stderr,
91 "Alignment must be a postive, power of two, and multiple "
92 "of sizeof (void *).\n");
93 return (1);
94 }
95 return (0);
96 }
97
98 static void
parse_options(int argc,char * argv[])99 parse_options(int argc, char *argv[])
100 {
101 int c;
102 int errflag = 0;
103
104 execname = argv[0];
105 alignment = sysconf(_SC_PAGE_SIZE);
106
107 extern char *optarg;
108 extern int optind, optopt;
109
110 while ((c = getopt(argc, argv, "a:b:c:deDi:o:s:k:Kp:P")) != -1) {
111 switch (c) {
112 case 'a':
113 alignment = atoi(optarg);
114 break;
115
116 case 'b':
117 bsize = atoi(optarg);
118 break;
119
120 case 'c':
121 count = atoi(optarg);
122 break;
123
124 case 'd':
125 if_o_direct = 1;
126 break;
127
128 case 'e':
129 entire_file = 1;
130 break;
131
132 case 'D':
133 of_o_direct = 1;
134 break;
135
136 case 'i':
137 ifile = optarg;
138 break;
139
140 case 'o':
141 ofile = optarg;
142 break;
143
144 case 's':
145 stride = atoi(optarg);
146 break;
147
148 case 'k':
149 seek = atoi(optarg);
150 break;
151
152 case 'K':
153 seekbytes = 1;
154 break;
155
156 case 'p':
157 skip = atoi(optarg);
158 break;
159
160 case 'P':
161 skipbytes = 1;
162 break;
163
164 case ':':
165 (void) fprintf(stderr,
166 "Option -%c requires an operand\n", optopt);
167 errflag++;
168 break;
169
170 case '?':
171 default:
172 (void) fprintf(stderr,
173 "Unrecognized option: -%c\n", optopt);
174 errflag++;
175 break;
176 }
177
178 if (errflag) {
179 (void) usage();
180 }
181 }
182
183 if (bsize <= 0 || stride <= 0 || ifile == NULL || ofile == NULL ||
184 seek < 0 || invalid_alignment(alignment) || skip < 0) {
185 (void) fprintf(stderr,
186 "Required parameter(s) missing or invalid.\n");
187 (void) usage();
188 }
189
190 if (count <= 0 && entire_file == 0) {
191 (void) fprintf(stderr,
192 "Required parameter(s) missing or invalid.\n");
193 (void) usage();
194 }
195 }
196
197 static void
read_entire_file(int ifd,int ofd,void * buf)198 read_entire_file(int ifd, int ofd, void *buf)
199 {
200 int c;
201
202 do {
203 c = read(ifd, buf, bsize);
204 if (c < 0) {
205 perror("read");
206 exit(2);
207 } else if (c != 0) {
208 c = write(ofd, buf, bsize);
209 if (c < 0) {
210 perror("write");
211 exit(2);
212 }
213
214 }
215 if (stride > 1) {
216 if (lseek(ifd, (stride - 1) * bsize, SEEK_CUR) == -1) {
217 perror("input lseek");
218 exit(2);
219 }
220 if (lseek(ofd, (stride - 1) * bsize, SEEK_CUR) == -1) {
221 perror("output lseek");
222 exit(2);
223 }
224 }
225 } while (c != 0);
226 }
227
228 static void
read_on_count(int ifd,int ofd,void * buf)229 read_on_count(int ifd, int ofd, void *buf)
230 {
231 int i;
232 int c;
233
234 for (i = 0; i < count; i++) {
235 c = read(ifd, buf, bsize);
236 if (c != bsize) {
237 if (c < 0) {
238 perror("read");
239 } else {
240 (void) fprintf(stderr,
241 "%s: unexpected short read, read %d "
242 "bytes, expected %d\n", execname,
243 c, bsize);
244 }
245 exit(2);
246 }
247
248 c = write(ofd, buf, bsize);
249 if (c != bsize) {
250 if (c < 0) {
251 perror("write");
252 } else {
253 (void) fprintf(stderr,
254 "%s: unexpected short write, wrote %d "
255 "bytes, expected %d\n", execname,
256 c, bsize);
257 }
258 exit(2);
259 }
260
261 if (stride > 1) {
262 if (lseek(ifd, (stride - 1) * bsize, SEEK_CUR) == -1) {
263 perror("input lseek");
264 exit(2);
265 }
266 if (lseek(ofd, (stride - 1) * bsize, SEEK_CUR) == -1) {
267 perror("output lseek");
268 exit(2);
269 }
270 }
271 }
272 }
273
274 int
main(int argc,char * argv[])275 main(int argc, char *argv[])
276 {
277 int ifd;
278 int ofd;
279 int ifd_flags = O_RDONLY;
280 int ofd_flags = O_WRONLY | O_CREAT;
281 void *buf;
282
283 parse_options(argc, argv);
284
285 if (if_o_direct)
286 ifd_flags |= O_DIRECT;
287
288 if (of_o_direct)
289 ofd_flags |= O_DIRECT;
290
291 ifd = open(ifile, ifd_flags);
292 if (ifd == -1) {
293 (void) fprintf(stderr, "%s: %s: ", execname, ifile);
294 perror("open");
295 exit(2);
296 }
297
298 ofd = open(ofile, ofd_flags, 0666);
299 if (ofd == -1) {
300 (void) fprintf(stderr, "%s: %s: ", execname, ofile);
301 perror("open");
302 exit(2);
303 }
304
305 /*
306 * We use valloc because some character block devices expect a
307 * page-aligned buffer.
308 */
309 int err = posix_memalign(&buf, alignment, bsize);
310 if (err != 0) {
311 (void) fprintf(stderr,
312 "%s: %s\n", execname, strerror(err));
313 exit(2);
314 }
315
316 if (skip > 0) {
317 int skipamt = skipbytes == 1 ? skip : skip * bsize;
318 if (lseek(ifd, skipamt, SEEK_CUR) == -1) {
319 perror("input lseek");
320 exit(2);
321 }
322 }
323
324 if (seek > 0) {
325 int seekamt = seekbytes == 1 ? seek : seek * bsize;
326 if (lseek(ofd, seekamt, SEEK_CUR) == -1) {
327 perror("output lseek");
328 exit(2);
329 }
330 }
331
332 if (entire_file == 1)
333 read_entire_file(ifd, ofd, buf);
334 else
335 read_on_count(ifd, ofd, buf);
336
337 free(buf);
338
339 (void) close(ofd);
340 (void) close(ifd);
341
342 return (0);
343 }
344