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