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