xref: /freebsd/sys/contrib/openzfs/tests/zfs-tests/cmd/stride_dd.c (revision 7a7741af18d6c8a804cc643cb7ecda9d730c6aa6)
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