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