| /* |
| * This file and its contents are supplied under the terms of the |
| * Common Development and Distribution License ("CDDL"), version 1.0. |
| * You may only use this file in accordance with the terms of version |
| * 1.0 of the CDDL. |
| * |
| * A full copy of the text of the CDDL should have accompanied this |
| * source. A copy of the CDDL is also available via the Internet at |
| * http://www.illumos.org/license/CDDL. |
| */ |
| |
| /* |
| * Copyright (c) 2018 by Delphix. All rights reserved. |
| */ |
| |
| #include <sys/types.h> |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <stdio.h> |
| #include <unistd.h> |
| #include <stdlib.h> |
| #include <string.h> |
| |
| static int bsize = 0; |
| static int count = 0; |
| static char *ifile = NULL; |
| static char *ofile = NULL; |
| static int stride = 0; |
| static int seek = 0; |
| static char *execname = "stride_dd"; |
| |
| static void usage(void); |
| static void parse_options(int argc, char *argv[]); |
| |
| static void |
| usage(void) |
| { |
| (void) fprintf(stderr, |
| "usage: %s -i inputfile -o outputfile -b blocksize -c count \n" |
| " -s stride [ -k seekblocks]\n" |
| "\n" |
| "Simplified version of dd that supports the stride option.\n" |
| "A stride of n means that for each block written, n - 1 blocks\n" |
| "are skipped in both the input and output file. A stride of 1\n" |
| "means that blocks are read and written consecutively.\n" |
| "All numeric parameters must be integers.\n" |
| "\n" |
| " inputfile: File to read from\n" |
| " outputfile: File to write to\n" |
| " blocksize: Size of each block to read/write\n" |
| " count: Number of blocks to read/write\n" |
| " stride: Read/write a block then skip (stride - 1) blocks\n" |
| " seekblocks: Number of blocks to skip at start of output\n", |
| execname); |
| (void) exit(1); |
| } |
| |
| static void |
| parse_options(int argc, char *argv[]) |
| { |
| int c; |
| int errflag = 0; |
| |
| execname = argv[0]; |
| |
| extern char *optarg; |
| extern int optind, optopt; |
| |
| while ((c = getopt(argc, argv, ":b:c:i:o:s:k:")) != -1) { |
| switch (c) { |
| case 'b': |
| bsize = atoi(optarg); |
| break; |
| |
| case 'c': |
| count = atoi(optarg); |
| break; |
| |
| case 'i': |
| ifile = optarg; |
| break; |
| |
| case 'o': |
| ofile = optarg; |
| break; |
| |
| case 's': |
| stride = atoi(optarg); |
| break; |
| |
| case 'k': |
| seek = atoi(optarg); |
| break; |
| |
| case ':': |
| (void) fprintf(stderr, |
| "Option -%c requires an operand\n", optopt); |
| errflag++; |
| break; |
| |
| case '?': |
| default: |
| (void) fprintf(stderr, |
| "Unrecognized option: -%c\n", optopt); |
| errflag++; |
| break; |
| } |
| |
| if (errflag) { |
| (void) usage(); |
| } |
| } |
| |
| if (bsize <= 0 || count <= 0 || stride <= 0 || ifile == NULL || |
| ofile == NULL || seek < 0) { |
| (void) fprintf(stderr, |
| "Required parameter(s) missing or invalid.\n"); |
| (void) usage(); |
| } |
| } |
| |
| int |
| main(int argc, char *argv[]) |
| { |
| int i; |
| int ifd; |
| int ofd; |
| void *buf; |
| int c; |
| |
| parse_options(argc, argv); |
| |
| ifd = open(ifile, O_RDONLY); |
| if (ifd == -1) { |
| (void) fprintf(stderr, "%s: %s: ", execname, ifile); |
| perror("open"); |
| exit(2); |
| } |
| |
| ofd = open(ofile, O_WRONLY | O_CREAT, 0666); |
| if (ofd == -1) { |
| (void) fprintf(stderr, "%s: %s: ", execname, ofile); |
| perror("open"); |
| exit(2); |
| } |
| |
| /* |
| * We use valloc because some character block devices expect a |
| * page-aligned buffer. |
| */ |
| int err = posix_memalign(&buf, 4096, bsize); |
| if (err != 0) { |
| (void) fprintf(stderr, |
| "%s: %s\n", execname, strerror(err)); |
| exit(2); |
| } |
| |
| if (seek > 0) { |
| if (lseek(ofd, seek * bsize, SEEK_CUR) == -1) { |
| perror("output lseek"); |
| exit(2); |
| } |
| } |
| |
| for (i = 0; i < count; i++) { |
| c = read(ifd, buf, bsize); |
| if (c != bsize) { |
| |
| perror("read"); |
| exit(2); |
| } |
| if (c != bsize) { |
| if (c < 0) { |
| perror("read"); |
| } else { |
| (void) fprintf(stderr, |
| "%s: unexpected short read, read %d " |
| "bytes, expected %d\n", execname, |
| c, bsize); |
| } |
| exit(2); |
| } |
| |
| c = write(ofd, buf, bsize); |
| if (c != bsize) { |
| if (c < 0) { |
| perror("write"); |
| } else { |
| (void) fprintf(stderr, |
| "%s: unexpected short write, wrote %d " |
| "bytes, expected %d\n", execname, |
| c, bsize); |
| } |
| exit(2); |
| } |
| |
| if (stride > 1) { |
| if (lseek(ifd, (stride - 1) * bsize, SEEK_CUR) == -1) { |
| perror("input lseek"); |
| exit(2); |
| } |
| if (lseek(ofd, (stride - 1) * bsize, SEEK_CUR) == -1) { |
| perror("output lseek"); |
| exit(2); |
| } |
| } |
| } |
| free(buf); |
| |
| (void) close(ofd); |
| (void) close(ifd); |
| |
| return (0); |
| } |