ubase

suckless linux base utils
git clone git://git.suckless.org/ubase
Log | Files | Refs | README | LICENSE

dd.c (6564B)


      1 /* (C) 2011-2012 Sebastian Krahmer all rights reserved.
      2  *
      3  * Optimized dd, to speed up backups etc.
      4  *
      5  * Permission has been granted to release this code under MIT/X.
      6  * The original code is at https://github.com/stealth/odd.  This
      7  * version of the code has been modified by sin@2f30.org.
      8  */
      9 #include <sys/ioctl.h>
     10 #include <sys/mount.h>
     11 #include <sys/stat.h>
     12 #include <sys/time.h>
     13 #include <sys/types.h>
     14 #include <sys/vfs.h>
     15 
     16 #include <errno.h>
     17 #include <fcntl.h>
     18 #include <inttypes.h>
     19 #include <signal.h>
     20 #include <stdio.h>
     21 #include <stdlib.h>
     22 #include <stdint.h>
     23 #include <string.h>
     24 #include <time.h>
     25 #include <unistd.h>
     26 
     27 #include "util.h"
     28 
     29 struct dd_config {
     30 	const char *in, *out;
     31 	uint64_t skip, seek, count, b_in, b_out, rec_in, rec_out;
     32 	off_t fsize;
     33 	blksize_t bs;
     34 	char quiet, nosync, notrunc, direct;
     35 	time_t t_start, t_end;
     36 };
     37 
     38 static int sigint = 0;
     39 
     40 static void
     41 sig_int(int unused_1, siginfo_t *unused_2, void *unused_3)
     42 {
     43 	(void) unused_1;
     44 	(void) unused_2;
     45 	(void) unused_3;
     46 	sigint = 1;
     47 }
     48 
     49 static int
     50 prepare_copy(struct dd_config *ddc, int *ifd, int *ofd)
     51 {
     52 	struct stat st;
     53 	int fli = O_RDONLY|O_LARGEFILE|O_NOCTTY, flo = O_WRONLY|O_LARGEFILE|O_CREAT|O_NOATIME|O_NOCTTY;
     54 	long pagesize;
     55 
     56 	if (ddc->direct) {
     57 		fli |= O_DIRECT;
     58 		flo |= O_DIRECT;
     59 	}
     60 
     61 	if (!ddc->in) *ifd = 0;
     62 	else if ((*ifd = open(ddc->in, fli)) < 0)
     63 		return -1;
     64 
     65 	if (fstat(*ifd, &st) < 0)
     66 		return -1;
     67 
     68 	ddc->fsize = st.st_size;
     69 
     70 	/* If "bsize" is not given, use optimum of both FS' */
     71 	if (!ddc->bs) {
     72 		struct statfs fst;
     73 		memset(&fst, 0, sizeof(fst));
     74 		pagesize = sysconf(_SC_PAGESIZE);
     75 		if (pagesize <= 0)
     76 			pagesize = 0x1000;
     77 		if (statfs(ddc->out, &fst) < 0 || fst.f_bsize == 0)
     78 			fst.f_bsize = pagesize;
     79 		if ((unsigned long)fst.f_bsize > (unsigned long)st.st_blksize)
     80 			ddc->bs = fst.f_bsize;
     81 		else
     82 			ddc->bs = st.st_blksize;
     83 		if (ddc->bs == 0)
     84 			ddc->bs = pagesize;
     85 	}
     86 
     87 	/* check if device or regular file */
     88 	if (!S_ISREG(st.st_mode)) {
     89 		if (S_ISBLK(st.st_mode)) {
     90 			if (ioctl(*ifd, BLKGETSIZE64, &ddc->fsize) < 0) {
     91 				close(*ifd);
     92 				return -1;
     93 			}
     94 		} else {
     95 			ddc->fsize = (off_t)-1;
     96 		}
     97 	}
     98 
     99 	/* skip and seek are in block items */
    100 	ddc->skip *= ddc->bs;
    101 	ddc->seek *= ddc->bs;
    102 
    103 	/* skip more bytes than are inside source file? */
    104 	if (ddc->fsize != (off_t)-1 && ddc->skip >= (uint64_t)ddc->fsize) {
    105 		errno = EINVAL;
    106 		close(*ifd);
    107 		return -1;
    108 	}
    109 
    110 	if (!ddc->seek && !ddc->notrunc)
    111 		flo |= O_TRUNC;
    112 
    113 	if (!ddc->out) *ofd = 1;
    114 	else if ((*ofd = open(ddc->out, flo, st.st_mode)) < 0) {
    115 		close(*ifd);
    116 		return -1;
    117 	}
    118 
    119 	if (ddc->seek && !ddc->notrunc) {
    120 		if (fstat(*ofd, &st) < 0)
    121 			return -1;
    122 		if (!S_ISREG(st.st_mode))
    123 			;
    124 		else if (ftruncate(*ofd, ddc->seek) < 0)
    125 			return -1;
    126 	}
    127 
    128 	if (lseek(*ifd, ddc->skip, SEEK_CUR) < 0) {
    129 		char buffer[ddc->bs];
    130 		for (uint64_t i = 0; i < ddc->skip; i += ddc->bs) {
    131 			if (read(*ifd, &buffer, ddc->bs) < 0) {
    132 				errno = EINVAL;
    133 				close(*ifd);
    134 				return -1;
    135 			}
    136 		}
    137 	}
    138 	lseek(*ofd, ddc->seek, SEEK_CUR);
    139 	posix_fadvise(*ifd, ddc->skip, 0, POSIX_FADV_SEQUENTIAL);
    140 	posix_fadvise(*ofd, 0, 0, POSIX_FADV_DONTNEED);
    141 
    142 	/* count is in block items too */
    143 	ddc->count *= ddc->bs;
    144 
    145 	/* If no count is given, its the filesize minus skip offset */
    146 	if (ddc->count == (uint64_t) -1)
    147 		ddc->count = ddc->fsize - ddc->skip;
    148 
    149 	return 0;
    150 }
    151 
    152 static int
    153 copy_splice(struct dd_config *ddc)
    154 {
    155 	int ifd, ofd, p[2] = {-1, -1};
    156 	ssize_t r = 0;
    157 	size_t n = 0;
    158 
    159 	if (prepare_copy(ddc, &ifd, &ofd) < 0)
    160 		return -1;
    161 	if (pipe(p) < 0) {
    162 		close(ifd); close(ofd);
    163 		close(p[0]); close(p[1]);
    164 		return -1;
    165 	}
    166 #ifdef F_SETPIPE_SZ
    167 	for (n = 29; n >= 20; --n) {
    168 		if (fcntl(p[0], F_SETPIPE_SZ, 1<<n) != -1)
    169 			break;
    170 	}
    171 	errno = 0;
    172 #endif
    173 	n = ddc->bs;
    174 	for (;ddc->b_out != ddc->count && !sigint;) {
    175 		if (r < 0)
    176 			break;
    177 		if (n > ddc->count - ddc->b_out)
    178 			n = ddc->count - ddc->b_out;
    179 		r = splice(ifd, NULL, p[1], NULL, n, SPLICE_F_MORE);
    180 		if (r <= 0)
    181 			break;
    182 		++ddc->rec_in;
    183 		r = splice(p[0], NULL, ofd, NULL, r, SPLICE_F_MORE);
    184 		if (r <= 0)
    185 			break;
    186 		ddc->b_out += r;
    187 		++ddc->rec_out;
    188 	}
    189 
    190 	if (sigint)
    191 		fprintf(stderr, "SIGINT! Aborting ...\n");
    192 
    193 	close(ifd);
    194 	close(ofd);
    195 	close(p[0]);
    196 	close(p[1]);
    197 	if (r < 0)
    198 		return -1;
    199 	return 0;
    200 }
    201 
    202 static int
    203 copy(struct dd_config *ddc)
    204 {
    205 	int r = 0;
    206 
    207 	ddc->t_start = time(NULL);
    208 
    209 	r = copy_splice(ddc);
    210 	ddc->t_end = time(NULL);
    211 
    212 	/* avoid div by zero */
    213 	if (ddc->t_start == ddc->t_end)
    214 		++ddc->t_end;
    215 	return r;
    216 }
    217 
    218 static void
    219 print_stat(const struct dd_config *ddc)
    220 {
    221 	if (ddc->quiet)
    222 		return;
    223 
    224 	fprintf(stderr, "%"PRIu64" records in\n", ddc->rec_in);
    225 	fprintf(stderr, "%"PRIu64" records out\n", ddc->rec_out);
    226 	fprintf(stderr, "%"PRIu64" bytes (%"PRIu64" MB) copied", ddc->b_out,
    227 		ddc->b_out/(1<<20));
    228 	fprintf(stderr, ", %lu s, %f MB/s\n",
    229 		(unsigned long)ddc->t_end - ddc->t_start,
    230 		((double)(ddc->b_out/(1<<20)))/(ddc->t_end - ddc->t_start));
    231 }
    232 
    233 static void
    234 usage(void)
    235 {
    236 	eprintf("usage: %s [-h] [if=infile] [of=outfile] [bs[=N]] [seek=N] "
    237 	        "[skip=N] [count=N] [direct] [quiet] [nosync]"
    238 		"[conv=notrunc]\n", argv0);
    239 }
    240 
    241 int
    242 main(int argc, char *argv[])
    243 {
    244 	int i = 0;
    245 	char buf[1024];
    246 	struct dd_config config;
    247 	struct sigaction sa;
    248 
    249 	argv0 = argv[0];
    250 	memset(&config, 0, sizeof(config));
    251 	config.bs = 1<<16;
    252 	config.in = NULL;
    253 	config.out = NULL;
    254 	config.count = (uint64_t) -1;
    255 
    256 	/* emulate 'dd' argument parsing */
    257 	for (i = 1; i < argc; ++i) {
    258 		memset(buf, 0, sizeof(buf));
    259 		if (strncmp(argv[i], "if=", 3) == 0)
    260 			config.in = argv[i]+3;
    261 		else if (strncmp(argv[i], "of=", 3) == 0)
    262 			config.out = argv[i]+3;
    263 		else if (sscanf(argv[i], "skip=%1023s", buf) == 1)
    264 			config.skip = estrtoul(buf, 0);
    265 		else if (sscanf(argv[i], "seek=%1023s", buf) == 1)
    266 			config.seek = estrtoul(buf, 0);
    267 		else if (sscanf(argv[i], "count=%1023s", buf) == 1)
    268 			config.count = estrtoul(buf, 0);
    269 		else if (strcmp(argv[i], "direct") == 0)
    270 			config.direct = 1;
    271 		else if (sscanf(argv[i], "bs=%1023s", buf) == 1)
    272 			config.bs = estrtoul(buf, 0);
    273 		else if (strcmp(argv[i], "bs") == 0)
    274 			config.bs = 0;
    275 		else if (strcmp(argv[i], "quiet") == 0)
    276 			config.quiet = 1;
    277 		else if (strcmp(argv[i], "nosync") == 0)
    278 			config.nosync = 1;
    279 		else if (strcmp(argv[i], "conv=notrunc") == 0)
    280 			config.notrunc = 1;
    281 		else if (strcmp(argv[i], "-h") == 0)
    282 			usage();
    283 	}
    284 
    285 	signal(SIGPIPE, SIG_IGN);
    286 
    287 	sa.sa_flags = SA_SIGINFO;
    288 	sigemptyset(&sa.sa_mask);
    289 	sa.sa_sigaction = sig_int;
    290 
    291 	if (sigaction(SIGINT, &sa, NULL) == -1)
    292 		weprintf("sigaction");
    293 
    294 	if (copy(&config) < 0)
    295 		weprintf("copy:");
    296 	print_stat(&config);
    297 
    298 	if (config.nosync == 0)
    299 		sync();
    300 	return errno;
    301 }