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 bsize = 0;
25 static int count = 0;
26 static char *ifile = NULL;
27 static char *ofile = NULL;
28 static int stride = 0;
29 static int seek = 0;
30 static const char *execname = "stride_dd";
31 
32 static void usage(void);
33 static void parse_options(int argc, char *argv[]);
34 
35 static void
36 usage(void)
37 {
38 	(void) fprintf(stderr,
39 	    "usage: %s -i inputfile -o outputfile -b blocksize -c count \n"
40 	    "           -s stride [ -k seekblocks]\n"
41 	    "\n"
42 	    "Simplified version of dd that supports the stride option.\n"
43 	    "A stride of n means that for each block written, n - 1 blocks\n"
44 	    "are skipped in both the input and output file. A stride of 1\n"
45 	    "means that blocks are read and written consecutively.\n"
46 	    "All numeric parameters must be integers.\n"
47 	    "\n"
48 	    "    inputfile:  File to read from\n"
49 	    "    outputfile: File to write to\n"
50 	    "    blocksize:  Size of each block to read/write\n"
51 	    "    count:      Number of blocks to read/write\n"
52 	    "    stride:     Read/write a block then skip (stride - 1) blocks\n"
53 	    "    seekblocks: Number of blocks to skip at start of output\n",
54 	    execname);
55 	(void) exit(1);
56 }
57 
58 static void
59 parse_options(int argc, char *argv[])
60 {
61 	int c;
62 	int errflag = 0;
63 
64 	execname = argv[0];
65 
66 	extern char *optarg;
67 	extern int optind, optopt;
68 
69 	while ((c = getopt(argc, argv, ":b:c:i:o:s:k:")) != -1) {
70 		switch (c) {
71 			case 'b':
72 				bsize = atoi(optarg);
73 				break;
74 
75 			case 'c':
76 				count = atoi(optarg);
77 				break;
78 
79 			case 'i':
80 				ifile = optarg;
81 				break;
82 
83 			case 'o':
84 				ofile = optarg;
85 				break;
86 
87 			case 's':
88 				stride = atoi(optarg);
89 				break;
90 
91 			case 'k':
92 				seek = atoi(optarg);
93 				break;
94 
95 			case ':':
96 				(void) fprintf(stderr,
97 				    "Option -%c requires an operand\n", optopt);
98 				errflag++;
99 				break;
100 
101 			case '?':
102 			default:
103 				(void) fprintf(stderr,
104 				    "Unrecognized option: -%c\n", optopt);
105 				errflag++;
106 				break;
107 		}
108 
109 		if (errflag) {
110 			(void) usage();
111 		}
112 	}
113 
114 	if (bsize <= 0 || count <= 0 || stride <= 0 || ifile == NULL ||
115 	    ofile == NULL || seek < 0) {
116 		(void) fprintf(stderr,
117 		    "Required parameter(s) missing or invalid.\n");
118 		(void) usage();
119 	}
120 }
121 
122 int
123 main(int argc, char *argv[])
124 {
125 	int i;
126 	int ifd;
127 	int ofd;
128 	void *buf;
129 	int c;
130 
131 	parse_options(argc, argv);
132 
133 	ifd = open(ifile, O_RDONLY);
134 	if (ifd == -1) {
135 		(void) fprintf(stderr, "%s: %s: ", execname, ifile);
136 		perror("open");
137 		exit(2);
138 	}
139 
140 	ofd = open(ofile, O_WRONLY | O_CREAT, 0666);
141 	if (ofd == -1) {
142 		(void) fprintf(stderr, "%s: %s: ", execname, ofile);
143 		perror("open");
144 		exit(2);
145 	}
146 
147 	/*
148 	 * We use valloc because some character block devices expect a
149 	 * page-aligned buffer.
150 	 */
151 	int err = posix_memalign(&buf, 4096, bsize);
152 	if (err != 0) {
153 		(void) fprintf(stderr,
154 		    "%s: %s\n", execname, strerror(err));
155 		exit(2);
156 	}
157 
158 	if (seek > 0) {
159 		if (lseek(ofd, seek * bsize, SEEK_CUR) == -1) {
160 			perror("output lseek");
161 			exit(2);
162 		}
163 	}
164 
165 	for (i = 0; i < count; i++) {
166 		c = read(ifd, buf, bsize);
167 		if (c != bsize) {
168 
169 			perror("read");
170 			exit(2);
171 		}
172 		if (c != bsize) {
173 			if (c < 0) {
174 				perror("read");
175 			} else {
176 				(void) fprintf(stderr,
177 				    "%s: unexpected short read, read %d "
178 				    "bytes, expected %d\n", execname,
179 				    c, bsize);
180 			}
181 			exit(2);
182 		}
183 
184 		c = write(ofd, buf, bsize);
185 		if (c != bsize) {
186 			if (c < 0) {
187 				perror("write");
188 			} else {
189 				(void) fprintf(stderr,
190 				    "%s: unexpected short write, wrote %d "
191 				    "bytes, expected %d\n", execname,
192 				    c, bsize);
193 			}
194 			exit(2);
195 		}
196 
197 		if (stride > 1) {
198 			if (lseek(ifd, (stride - 1) * bsize, SEEK_CUR) == -1) {
199 				perror("input lseek");
200 				exit(2);
201 			}
202 			if (lseek(ofd, (stride - 1) * bsize, SEEK_CUR) == -1) {
203 				perror("output lseek");
204 				exit(2);
205 			}
206 		}
207 	}
208 	free(buf);
209 
210 	(void) close(ofd);
211 	(void) close(ifd);
212 
213 	return (0);
214 }
215