mirror of
				https://git.proxmox.com/git/mirror_zfs.git
				synced 2025-10-26 18:05:04 +03:00 
			
		
		
		
	Sponsored-by: https://despairlabs.com/sponsor/ Signed-off-by: Rob Norris <robn@despairlabs.com> Reviewed-by: Tony Hutter <hutter2@llnl.gov> Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
		
			
				
	
	
		
			345 lines
		
	
	
		
			7.2 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			345 lines
		
	
	
		
			7.2 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
// SPDX-License-Identifier: CDDL-1.0
 | 
						|
/*
 | 
						|
 * 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 alignment = 0;
 | 
						|
static int bsize = 0;
 | 
						|
static int count = 0;
 | 
						|
static char *ifile = NULL;
 | 
						|
static char *ofile = NULL;
 | 
						|
static off_t stride = 1;
 | 
						|
static off_t seek = 0;
 | 
						|
static int seekbytes = 0;
 | 
						|
static int if_o_direct = 0;
 | 
						|
static int of_o_direct = 0;
 | 
						|
static int skip = 0;
 | 
						|
static int skipbytes = 0;
 | 
						|
static int entire_file = 0;
 | 
						|
static const 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] [-K seekbytes]\n"
 | 
						|
	    "           [-a alignment] [-d if_o_direct] [-D of_o_direct]\n"
 | 
						|
	    "           [-p skipblocks] [-P skipbytes] [-e entire_file]\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 (Required"
 | 
						|
	    " unless -e is used)\n"
 | 
						|
	    "    stride:      Read/write a block then skip (stride - 1) blocks"
 | 
						|
	    "\n"
 | 
						|
	    "    seekblocks:  Number of blocks to skip at start of output\n"
 | 
						|
	    "    seekbytes:   Treat seekblocks as byte count\n"
 | 
						|
	    "    alignment:   Alignment passed to posix_memalign() (default"
 | 
						|
	    " PAGE_SIZE)\n"
 | 
						|
	    "    if_o_direct: Use O_DIRECT with inputfile (default no O_DIRECT)"
 | 
						|
	    "\n"
 | 
						|
	    "    of_o_direct: Use O_DIRECT with outputfile (default no "
 | 
						|
	    " O_DIRECT)\n"
 | 
						|
	    "    skipblocks:  Number of blocks to skip at start of input "
 | 
						|
	    " (default 0)\n"
 | 
						|
	    "    skipbytes:   Treat skipblocks as byte count\n"
 | 
						|
	    "    entire_file: When used the entire inputfile will be read and"
 | 
						|
	    " count will be ignored\n",
 | 
						|
	    execname);
 | 
						|
	(void) exit(1);
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * posix_memalign() only allows for alignments which are postive, powers of two
 | 
						|
 * and a multiple of sizeof (void *).
 | 
						|
 */
 | 
						|
static int
 | 
						|
invalid_alignment(int alignment)
 | 
						|
{
 | 
						|
	if ((alignment < 0) || (alignment & (alignment - 1)) ||
 | 
						|
	    ((alignment % sizeof (void *)))) {
 | 
						|
		(void) fprintf(stderr,
 | 
						|
		    "Alignment must be a postive, power of two, and multiple "
 | 
						|
		    "of sizeof (void *).\n");
 | 
						|
		return (1);
 | 
						|
	}
 | 
						|
	return (0);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
parse_options(int argc, char *argv[])
 | 
						|
{
 | 
						|
	int c;
 | 
						|
	int errflag = 0;
 | 
						|
 | 
						|
	execname = argv[0];
 | 
						|
	alignment = sysconf(_SC_PAGE_SIZE);
 | 
						|
 | 
						|
	extern char *optarg;
 | 
						|
	extern int optind, optopt;
 | 
						|
 | 
						|
	while ((c = getopt(argc, argv, "a:b:c:deDi:o:s:k:Kp:P")) != -1) {
 | 
						|
		switch (c) {
 | 
						|
			case 'a':
 | 
						|
				alignment = atoi(optarg);
 | 
						|
				break;
 | 
						|
 | 
						|
			case 'b':
 | 
						|
				bsize = atoi(optarg);
 | 
						|
				break;
 | 
						|
 | 
						|
			case 'c':
 | 
						|
				count = atoi(optarg);
 | 
						|
				break;
 | 
						|
 | 
						|
			case 'd':
 | 
						|
				if_o_direct = 1;
 | 
						|
				break;
 | 
						|
 | 
						|
			case 'e':
 | 
						|
				entire_file = 1;
 | 
						|
				break;
 | 
						|
 | 
						|
			case 'D':
 | 
						|
				of_o_direct = 1;
 | 
						|
				break;
 | 
						|
 | 
						|
			case 'i':
 | 
						|
				ifile = optarg;
 | 
						|
				break;
 | 
						|
 | 
						|
			case 'o':
 | 
						|
				ofile = optarg;
 | 
						|
				break;
 | 
						|
 | 
						|
			case 's':
 | 
						|
				stride = atoi(optarg);
 | 
						|
				break;
 | 
						|
 | 
						|
			case 'k':
 | 
						|
				seek = atoi(optarg);
 | 
						|
				break;
 | 
						|
 | 
						|
			case 'K':
 | 
						|
				seekbytes = 1;
 | 
						|
				break;
 | 
						|
 | 
						|
			case 'p':
 | 
						|
				skip = atoi(optarg);
 | 
						|
				break;
 | 
						|
 | 
						|
			case 'P':
 | 
						|
				skipbytes = 1;
 | 
						|
				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 || stride <= 0 || ifile == NULL || ofile == NULL ||
 | 
						|
	    seek < 0 || invalid_alignment(alignment) || skip < 0) {
 | 
						|
		(void) fprintf(stderr,
 | 
						|
		    "Required parameter(s) missing or invalid.\n");
 | 
						|
		(void) usage();
 | 
						|
	}
 | 
						|
 | 
						|
	if (count <= 0 && entire_file == 0) {
 | 
						|
		(void) fprintf(stderr,
 | 
						|
		    "Required parameter(s) missing or invalid.\n");
 | 
						|
		(void) usage();
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
read_entire_file(int ifd, int ofd, void *buf)
 | 
						|
{
 | 
						|
	int c;
 | 
						|
 | 
						|
	do {
 | 
						|
		c = read(ifd, buf, bsize);
 | 
						|
		if (c < 0) {
 | 
						|
			perror("read");
 | 
						|
			exit(2);
 | 
						|
		} else if (c != 0) {
 | 
						|
			c = write(ofd, buf, bsize);
 | 
						|
			if (c < 0) {
 | 
						|
				perror("write");
 | 
						|
				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);
 | 
						|
			}
 | 
						|
		}
 | 
						|
	} while (c != 0);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
read_on_count(int ifd, int ofd, void *buf)
 | 
						|
{
 | 
						|
	int i;
 | 
						|
	int c;
 | 
						|
 | 
						|
	for (i = 0; i < count; i++) {
 | 
						|
		c = read(ifd, buf, bsize);
 | 
						|
		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);
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
main(int argc, char *argv[])
 | 
						|
{
 | 
						|
	int ifd;
 | 
						|
	int ofd;
 | 
						|
	int ifd_flags = O_RDONLY;
 | 
						|
	int ofd_flags = O_WRONLY | O_CREAT;
 | 
						|
	void *buf;
 | 
						|
 | 
						|
	parse_options(argc, argv);
 | 
						|
 | 
						|
	if (if_o_direct)
 | 
						|
		ifd_flags |= O_DIRECT;
 | 
						|
 | 
						|
	if (of_o_direct)
 | 
						|
		ofd_flags |= O_DIRECT;
 | 
						|
 | 
						|
	ifd = open(ifile, ifd_flags);
 | 
						|
	if (ifd == -1) {
 | 
						|
		(void) fprintf(stderr, "%s: %s: ", execname, ifile);
 | 
						|
		perror("open");
 | 
						|
		exit(2);
 | 
						|
	}
 | 
						|
 | 
						|
	ofd = open(ofile, ofd_flags, 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, alignment, bsize);
 | 
						|
	if (err != 0) {
 | 
						|
		(void) fprintf(stderr,
 | 
						|
		    "%s: %s\n", execname, strerror(err));
 | 
						|
		exit(2);
 | 
						|
	}
 | 
						|
 | 
						|
	if (skip > 0) {
 | 
						|
		int skipamt = skipbytes == 1 ? skip : skip * bsize;
 | 
						|
		if (lseek(ifd, skipamt, SEEK_CUR) == -1) {
 | 
						|
			perror("input lseek");
 | 
						|
			exit(2);
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if (seek > 0) {
 | 
						|
		int seekamt = seekbytes == 1 ? seek : seek * bsize;
 | 
						|
		if (lseek(ofd, seekamt, SEEK_CUR) == -1) {
 | 
						|
			perror("output lseek");
 | 
						|
			exit(2);
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if (entire_file == 1)
 | 
						|
		read_entire_file(ifd, ofd, buf);
 | 
						|
	else
 | 
						|
		read_on_count(ifd, ofd, buf);
 | 
						|
 | 
						|
	free(buf);
 | 
						|
 | 
						|
	(void) close(ofd);
 | 
						|
	(void) close(ifd);
 | 
						|
 | 
						|
	return (0);
 | 
						|
}
 |