xref: /freebsd-src/sys/contrib/openzfs/tests/zfs-tests/cmd/stride_dd.c (revision 7a7741af18d6c8a804cc643cb7ecda9d730c6aa6)
1716fd348SMartin Matuska /*
2716fd348SMartin Matuska  * This file and its contents are supplied under the terms of the
3716fd348SMartin Matuska  * Common Development and Distribution License ("CDDL"), version 1.0.
4716fd348SMartin Matuska  * You may only use this file in accordance with the terms of version
5716fd348SMartin Matuska  * 1.0 of the CDDL.
6716fd348SMartin Matuska  *
7716fd348SMartin Matuska  * A full copy of the text of the CDDL should have accompanied this
8716fd348SMartin Matuska  * source.  A copy of the CDDL is also available via the Internet at
9716fd348SMartin Matuska  * http://www.illumos.org/license/CDDL.
10716fd348SMartin Matuska  */
11716fd348SMartin Matuska 
12716fd348SMartin Matuska /*
13716fd348SMartin Matuska  * Copyright (c) 2018 by Delphix. All rights reserved.
14716fd348SMartin Matuska  */
15716fd348SMartin Matuska 
16716fd348SMartin Matuska #include <sys/types.h>
17716fd348SMartin Matuska #include <errno.h>
18716fd348SMartin Matuska #include <fcntl.h>
19716fd348SMartin Matuska #include <stdio.h>
20716fd348SMartin Matuska #include <unistd.h>
21716fd348SMartin Matuska #include <stdlib.h>
22716fd348SMartin Matuska #include <string.h>
23716fd348SMartin Matuska 
24*7a7741afSMartin Matuska static int alignment = 0;
25716fd348SMartin Matuska static int bsize = 0;
26716fd348SMartin Matuska static int count = 0;
27716fd348SMartin Matuska static char *ifile = NULL;
28716fd348SMartin Matuska static char *ofile = NULL;
29*7a7741afSMartin Matuska static off_t stride = 1;
30dbd5678dSMartin Matuska static off_t seek = 0;
31*7a7741afSMartin Matuska static int seekbytes = 0;
32*7a7741afSMartin Matuska static int if_o_direct = 0;
33*7a7741afSMartin Matuska static int of_o_direct = 0;
34*7a7741afSMartin Matuska static int skip = 0;
35*7a7741afSMartin Matuska static int skipbytes = 0;
36*7a7741afSMartin Matuska static int entire_file = 0;
37a0b956f5SMartin Matuska static const char *execname = "stride_dd";
38716fd348SMartin Matuska 
39716fd348SMartin Matuska static void usage(void);
40716fd348SMartin Matuska static void parse_options(int argc, char *argv[]);
41716fd348SMartin Matuska 
42716fd348SMartin Matuska static void
43716fd348SMartin Matuska usage(void)
44716fd348SMartin Matuska {
45716fd348SMartin Matuska 	(void) fprintf(stderr,
46*7a7741afSMartin Matuska 	    "usage: %s -i inputfile -o outputfile -b blocksize [-c count]\n"
47*7a7741afSMartin Matuska 	    "           [-s stride] [-k seekblocks] [-K seekbytes]\n"
48*7a7741afSMartin Matuska 	    "           [-a alignment] [-d if_o_direct] [-D of_o_direct]\n"
49*7a7741afSMartin Matuska 	    "           [-p skipblocks] [-P skipbytes] [-e entire_file]\n"
50716fd348SMartin Matuska 	    "\n"
51716fd348SMartin Matuska 	    "Simplified version of dd that supports the stride option.\n"
52716fd348SMartin Matuska 	    "A stride of n means that for each block written, n - 1 blocks\n"
53716fd348SMartin Matuska 	    "are skipped in both the input and output file. A stride of 1\n"
54716fd348SMartin Matuska 	    "means that blocks are read and written consecutively.\n"
55716fd348SMartin Matuska 	    "All numeric parameters must be integers.\n"
56716fd348SMartin Matuska 	    "\n"
57716fd348SMartin Matuska 	    "    inputfile:   File to read from\n"
58716fd348SMartin Matuska 	    "    outputfile:  File to write to\n"
59716fd348SMartin Matuska 	    "    blocksize:   Size of each block to read/write\n"
60*7a7741afSMartin Matuska 	    "    count:       Number of blocks to read/write (Required"
61*7a7741afSMartin Matuska 	    " unless -e is used)\n"
62*7a7741afSMartin Matuska 	    "    stride:      Read/write a block then skip (stride - 1) blocks"
63*7a7741afSMartin Matuska 	    "\n"
64*7a7741afSMartin Matuska 	    "    seekblocks:  Number of blocks to skip at start of output\n"
65*7a7741afSMartin Matuska 	    "    seekbytes:   Treat seekblocks as byte count\n"
66*7a7741afSMartin Matuska 	    "    alignment:   Alignment passed to posix_memalign() (default"
67*7a7741afSMartin Matuska 	    " PAGE_SIZE)\n"
68*7a7741afSMartin Matuska 	    "    if_o_direct: Use O_DIRECT with inputfile (default no O_DIRECT)"
69*7a7741afSMartin Matuska 	    "\n"
70*7a7741afSMartin Matuska 	    "    of_o_direct: Use O_DIRECT with outputfile (default no "
71*7a7741afSMartin Matuska 	    " O_DIRECT)\n"
72*7a7741afSMartin Matuska 	    "    skipblocks:  Number of blocks to skip at start of input "
73*7a7741afSMartin Matuska 	    " (default 0)\n"
74*7a7741afSMartin Matuska 	    "    skipbytes:   Treat skipblocks as byte count\n"
75*7a7741afSMartin Matuska 	    "    entire_file: When used the entire inputfile will be read and"
76*7a7741afSMartin Matuska 	    " count will be ignored\n",
77716fd348SMartin Matuska 	    execname);
78716fd348SMartin Matuska 	(void) exit(1);
79716fd348SMartin Matuska }
80716fd348SMartin Matuska 
81*7a7741afSMartin Matuska /*
82*7a7741afSMartin Matuska  * posix_memalign() only allows for alignments which are postive, powers of two
83*7a7741afSMartin Matuska  * and a multiple of sizeof (void *).
84*7a7741afSMartin Matuska  */
85*7a7741afSMartin Matuska static int
86*7a7741afSMartin Matuska invalid_alignment(int alignment)
87*7a7741afSMartin Matuska {
88*7a7741afSMartin Matuska 	if ((alignment < 0) || (alignment & (alignment - 1)) ||
89*7a7741afSMartin Matuska 	    ((alignment % sizeof (void *)))) {
90*7a7741afSMartin Matuska 		(void) fprintf(stderr,
91*7a7741afSMartin Matuska 		    "Alignment must be a postive, power of two, and multiple "
92*7a7741afSMartin Matuska 		    "of sizeof (void *).\n");
93*7a7741afSMartin Matuska 		return (1);
94*7a7741afSMartin Matuska 	}
95*7a7741afSMartin Matuska 	return (0);
96*7a7741afSMartin Matuska }
97*7a7741afSMartin Matuska 
98716fd348SMartin Matuska static void
99716fd348SMartin Matuska parse_options(int argc, char *argv[])
100716fd348SMartin Matuska {
101716fd348SMartin Matuska 	int c;
102716fd348SMartin Matuska 	int errflag = 0;
103716fd348SMartin Matuska 
104716fd348SMartin Matuska 	execname = argv[0];
105*7a7741afSMartin Matuska 	alignment = sysconf(_SC_PAGE_SIZE);
106716fd348SMartin Matuska 
107716fd348SMartin Matuska 	extern char *optarg;
108716fd348SMartin Matuska 	extern int optind, optopt;
109716fd348SMartin Matuska 
110*7a7741afSMartin Matuska 	while ((c = getopt(argc, argv, "a:b:c:deDi:o:s:k:Kp:P")) != -1) {
111716fd348SMartin Matuska 		switch (c) {
112*7a7741afSMartin Matuska 			case 'a':
113*7a7741afSMartin Matuska 				alignment = atoi(optarg);
114*7a7741afSMartin Matuska 				break;
115*7a7741afSMartin Matuska 
116716fd348SMartin Matuska 			case 'b':
117716fd348SMartin Matuska 				bsize = atoi(optarg);
118716fd348SMartin Matuska 				break;
119716fd348SMartin Matuska 
120716fd348SMartin Matuska 			case 'c':
121716fd348SMartin Matuska 				count = atoi(optarg);
122716fd348SMartin Matuska 				break;
123716fd348SMartin Matuska 
124*7a7741afSMartin Matuska 			case 'd':
125*7a7741afSMartin Matuska 				if_o_direct = 1;
126*7a7741afSMartin Matuska 				break;
127*7a7741afSMartin Matuska 
128*7a7741afSMartin Matuska 			case 'e':
129*7a7741afSMartin Matuska 				entire_file = 1;
130*7a7741afSMartin Matuska 				break;
131*7a7741afSMartin Matuska 
132*7a7741afSMartin Matuska 			case 'D':
133*7a7741afSMartin Matuska 				of_o_direct = 1;
134*7a7741afSMartin Matuska 				break;
135*7a7741afSMartin Matuska 
136716fd348SMartin Matuska 			case 'i':
137716fd348SMartin Matuska 				ifile = optarg;
138716fd348SMartin Matuska 				break;
139716fd348SMartin Matuska 
140716fd348SMartin Matuska 			case 'o':
141716fd348SMartin Matuska 				ofile = optarg;
142716fd348SMartin Matuska 				break;
143716fd348SMartin Matuska 
144716fd348SMartin Matuska 			case 's':
145716fd348SMartin Matuska 				stride = atoi(optarg);
146716fd348SMartin Matuska 				break;
147716fd348SMartin Matuska 
148716fd348SMartin Matuska 			case 'k':
149716fd348SMartin Matuska 				seek = atoi(optarg);
150716fd348SMartin Matuska 				break;
151716fd348SMartin Matuska 
152*7a7741afSMartin Matuska 			case 'K':
153*7a7741afSMartin Matuska 				seekbytes = 1;
154*7a7741afSMartin Matuska 				break;
155*7a7741afSMartin Matuska 
156*7a7741afSMartin Matuska 			case 'p':
157*7a7741afSMartin Matuska 				skip = atoi(optarg);
158*7a7741afSMartin Matuska 				break;
159*7a7741afSMartin Matuska 
160*7a7741afSMartin Matuska 			case 'P':
161*7a7741afSMartin Matuska 				skipbytes = 1;
162*7a7741afSMartin Matuska 				break;
163*7a7741afSMartin Matuska 
164716fd348SMartin Matuska 			case ':':
165716fd348SMartin Matuska 				(void) fprintf(stderr,
166716fd348SMartin Matuska 				    "Option -%c requires an operand\n", optopt);
167716fd348SMartin Matuska 				errflag++;
168716fd348SMartin Matuska 				break;
169716fd348SMartin Matuska 
170716fd348SMartin Matuska 			case '?':
171716fd348SMartin Matuska 			default:
172716fd348SMartin Matuska 				(void) fprintf(stderr,
173716fd348SMartin Matuska 				    "Unrecognized option: -%c\n", optopt);
174716fd348SMartin Matuska 				errflag++;
175716fd348SMartin Matuska 				break;
176716fd348SMartin Matuska 		}
177716fd348SMartin Matuska 
178716fd348SMartin Matuska 		if (errflag) {
179716fd348SMartin Matuska 			(void) usage();
180716fd348SMartin Matuska 		}
181716fd348SMartin Matuska 	}
182716fd348SMartin Matuska 
183*7a7741afSMartin Matuska 	if (bsize <= 0 || stride <= 0 || ifile == NULL || ofile == NULL ||
184*7a7741afSMartin Matuska 	    seek < 0 || invalid_alignment(alignment) || skip < 0) {
185*7a7741afSMartin Matuska 		(void) fprintf(stderr,
186*7a7741afSMartin Matuska 		    "Required parameter(s) missing or invalid.\n");
187*7a7741afSMartin Matuska 		(void) usage();
188*7a7741afSMartin Matuska 	}
189*7a7741afSMartin Matuska 
190*7a7741afSMartin Matuska 	if (count <= 0 && entire_file == 0) {
191716fd348SMartin Matuska 		(void) fprintf(stderr,
192716fd348SMartin Matuska 		    "Required parameter(s) missing or invalid.\n");
193716fd348SMartin Matuska 		(void) usage();
194716fd348SMartin Matuska 	}
195716fd348SMartin Matuska }
196716fd348SMartin Matuska 
197*7a7741afSMartin Matuska static void
198*7a7741afSMartin Matuska read_entire_file(int ifd, int ofd, void *buf)
199716fd348SMartin Matuska {
200716fd348SMartin Matuska 	int c;
201716fd348SMartin Matuska 
202*7a7741afSMartin Matuska 	do {
203*7a7741afSMartin Matuska 		c = read(ifd, buf, bsize);
204*7a7741afSMartin Matuska 		if (c < 0) {
205*7a7741afSMartin Matuska 			perror("read");
206*7a7741afSMartin Matuska 			exit(2);
207*7a7741afSMartin Matuska 		} else if (c != 0) {
208*7a7741afSMartin Matuska 			c = write(ofd, buf, bsize);
209*7a7741afSMartin Matuska 			if (c < 0) {
210*7a7741afSMartin Matuska 				perror("write");
211716fd348SMartin Matuska 				exit(2);
212716fd348SMartin Matuska 			}
213716fd348SMartin Matuska 
214*7a7741afSMartin Matuska 		}
215*7a7741afSMartin Matuska 		if (stride > 1) {
216*7a7741afSMartin Matuska 			if (lseek(ifd, (stride - 1) * bsize, SEEK_CUR) == -1) {
217*7a7741afSMartin Matuska 				perror("input lseek");
218716fd348SMartin Matuska 				exit(2);
219716fd348SMartin Matuska 			}
220*7a7741afSMartin Matuska 			if (lseek(ofd, (stride - 1) * bsize, SEEK_CUR) == -1) {
221716fd348SMartin Matuska 				perror("output lseek");
222716fd348SMartin Matuska 				exit(2);
223716fd348SMartin Matuska 			}
224716fd348SMartin Matuska 		}
225*7a7741afSMartin Matuska 	} while (c != 0);
226*7a7741afSMartin Matuska }
227*7a7741afSMartin Matuska 
228*7a7741afSMartin Matuska static void
229*7a7741afSMartin Matuska read_on_count(int ifd, int ofd, void *buf)
230*7a7741afSMartin Matuska {
231*7a7741afSMartin Matuska 	int i;
232*7a7741afSMartin Matuska 	int c;
233716fd348SMartin Matuska 
234716fd348SMartin Matuska 	for (i = 0; i < count; i++) {
235716fd348SMartin Matuska 		c = read(ifd, buf, bsize);
236716fd348SMartin Matuska 		if (c != bsize) {
237716fd348SMartin Matuska 			if (c < 0) {
238716fd348SMartin Matuska 				perror("read");
239716fd348SMartin Matuska 			} else {
240716fd348SMartin Matuska 				(void) fprintf(stderr,
241716fd348SMartin Matuska 				    "%s: unexpected short read, read %d "
242716fd348SMartin Matuska 				    "bytes, expected %d\n", execname,
243716fd348SMartin Matuska 				    c, bsize);
244716fd348SMartin Matuska 			}
245716fd348SMartin Matuska 			exit(2);
246716fd348SMartin Matuska 		}
247716fd348SMartin Matuska 
248716fd348SMartin Matuska 		c = write(ofd, buf, bsize);
249716fd348SMartin Matuska 		if (c != bsize) {
250716fd348SMartin Matuska 			if (c < 0) {
251716fd348SMartin Matuska 				perror("write");
252716fd348SMartin Matuska 			} else {
253716fd348SMartin Matuska 				(void) fprintf(stderr,
254716fd348SMartin Matuska 				    "%s: unexpected short write, wrote %d "
255716fd348SMartin Matuska 				    "bytes, expected %d\n", execname,
256716fd348SMartin Matuska 				    c, bsize);
257716fd348SMartin Matuska 			}
258716fd348SMartin Matuska 			exit(2);
259716fd348SMartin Matuska 		}
260716fd348SMartin Matuska 
261716fd348SMartin Matuska 		if (stride > 1) {
262716fd348SMartin Matuska 			if (lseek(ifd, (stride - 1) * bsize, SEEK_CUR) == -1) {
263716fd348SMartin Matuska 				perror("input lseek");
264716fd348SMartin Matuska 				exit(2);
265716fd348SMartin Matuska 			}
266716fd348SMartin Matuska 			if (lseek(ofd, (stride - 1) * bsize, SEEK_CUR) == -1) {
267716fd348SMartin Matuska 				perror("output lseek");
268716fd348SMartin Matuska 				exit(2);
269716fd348SMartin Matuska 			}
270716fd348SMartin Matuska 		}
271716fd348SMartin Matuska 	}
272*7a7741afSMartin Matuska }
273*7a7741afSMartin Matuska 
274*7a7741afSMartin Matuska int
275*7a7741afSMartin Matuska main(int argc, char *argv[])
276*7a7741afSMartin Matuska {
277*7a7741afSMartin Matuska 	int ifd;
278*7a7741afSMartin Matuska 	int ofd;
279*7a7741afSMartin Matuska 	int ifd_flags = O_RDONLY;
280*7a7741afSMartin Matuska 	int ofd_flags = O_WRONLY | O_CREAT;
281*7a7741afSMartin Matuska 	void *buf;
282*7a7741afSMartin Matuska 
283*7a7741afSMartin Matuska 	parse_options(argc, argv);
284*7a7741afSMartin Matuska 
285*7a7741afSMartin Matuska 	if (if_o_direct)
286*7a7741afSMartin Matuska 		ifd_flags |= O_DIRECT;
287*7a7741afSMartin Matuska 
288*7a7741afSMartin Matuska 	if (of_o_direct)
289*7a7741afSMartin Matuska 		ofd_flags |= O_DIRECT;
290*7a7741afSMartin Matuska 
291*7a7741afSMartin Matuska 	ifd = open(ifile, ifd_flags);
292*7a7741afSMartin Matuska 	if (ifd == -1) {
293*7a7741afSMartin Matuska 		(void) fprintf(stderr, "%s: %s: ", execname, ifile);
294*7a7741afSMartin Matuska 		perror("open");
295*7a7741afSMartin Matuska 		exit(2);
296*7a7741afSMartin Matuska 	}
297*7a7741afSMartin Matuska 
298*7a7741afSMartin Matuska 	ofd = open(ofile, ofd_flags, 0666);
299*7a7741afSMartin Matuska 	if (ofd == -1) {
300*7a7741afSMartin Matuska 		(void) fprintf(stderr, "%s: %s: ", execname, ofile);
301*7a7741afSMartin Matuska 		perror("open");
302*7a7741afSMartin Matuska 		exit(2);
303*7a7741afSMartin Matuska 	}
304*7a7741afSMartin Matuska 
305*7a7741afSMartin Matuska 	/*
306*7a7741afSMartin Matuska 	 * We use valloc because some character block devices expect a
307*7a7741afSMartin Matuska 	 * page-aligned buffer.
308*7a7741afSMartin Matuska 	 */
309*7a7741afSMartin Matuska 	int err = posix_memalign(&buf, alignment, bsize);
310*7a7741afSMartin Matuska 	if (err != 0) {
311*7a7741afSMartin Matuska 		(void) fprintf(stderr,
312*7a7741afSMartin Matuska 		    "%s: %s\n", execname, strerror(err));
313*7a7741afSMartin Matuska 		exit(2);
314*7a7741afSMartin Matuska 	}
315*7a7741afSMartin Matuska 
316*7a7741afSMartin Matuska 	if (skip > 0) {
317*7a7741afSMartin Matuska 		int skipamt = skipbytes == 1 ? skip : skip * bsize;
318*7a7741afSMartin Matuska 		if (lseek(ifd, skipamt, SEEK_CUR) == -1) {
319*7a7741afSMartin Matuska 			perror("input lseek");
320*7a7741afSMartin Matuska 			exit(2);
321*7a7741afSMartin Matuska 		}
322*7a7741afSMartin Matuska 	}
323*7a7741afSMartin Matuska 
324*7a7741afSMartin Matuska 	if (seek > 0) {
325*7a7741afSMartin Matuska 		int seekamt = seekbytes == 1 ? seek : seek * bsize;
326*7a7741afSMartin Matuska 		if (lseek(ofd, seekamt, SEEK_CUR) == -1) {
327*7a7741afSMartin Matuska 			perror("output lseek");
328*7a7741afSMartin Matuska 			exit(2);
329*7a7741afSMartin Matuska 		}
330*7a7741afSMartin Matuska 	}
331*7a7741afSMartin Matuska 
332*7a7741afSMartin Matuska 	if (entire_file == 1)
333*7a7741afSMartin Matuska 		read_entire_file(ifd, ofd, buf);
334*7a7741afSMartin Matuska 	else
335*7a7741afSMartin Matuska 		read_on_count(ifd, ofd, buf);
336*7a7741afSMartin Matuska 
337716fd348SMartin Matuska 	free(buf);
338716fd348SMartin Matuska 
339716fd348SMartin Matuska 	(void) close(ofd);
340716fd348SMartin Matuska 	(void) close(ifd);
341716fd348SMartin Matuska 
342716fd348SMartin Matuska 	return (0);
343716fd348SMartin Matuska }
344