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 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 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 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 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 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 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