ubase

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

commit 207ba019fda48ef7d84484afbfd56d327dd5e249
parent d2dfc053c14dab04870b5520bba42c9a0173c654
Author: sin <sin@2f30.org>
Date:   Wed Jun  4 11:39:00 +0100

Add initial version of dd(1).

This code was written by Sebastian Krahmer and you can find
the original version at https://github.com/stealth/odd.git.

Permission has been granted to release this code under MIT/X
for ubase.  It has been simplified by sin@2f30.org.

Thanks Sebastian!

Diffstat:
Makefile | 1+
dd.c | 269+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 270 insertions(+), 0 deletions(-)
diff --git a/Makefile b/Makefile @@ -22,6 +22,7 @@ SRC = \ chvt.c \ clear.c \ ctrlaltdel.c \ + dd.c \ df.c \ dmesg.c \ eject.c \ diff --git a/dd.c b/dd.c @@ -0,0 +1,269 @@ +/* (C) 2011-2012 Sebastian Krahmer all rights reserved. + * + * Optimized dd, to speed up backups etc. + * + * Permission has been granted to release this code under MIT/X. + * The original code is at https://github.com/stealth/odd. This + * version of the code has been modified by sin@2f30.org. + */ +#include <errno.h> +#include <fcntl.h> +#include <signal.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <stdint.h> +#include <sys/ioctl.h> +#include <sys/mount.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/vfs.h> +#include <time.h> +#include <unistd.h> + +struct dd_config { + const char *in, *out; + uint64_t skip, seek, count, b_in, b_out, rec_in, rec_out; + off_t fsize; + blksize_t bs; + char quiet, nosync, direct; + int saved_errno; + time_t t_start, t_end; +}; + +static int sigint = 0; + +static int +prepare_copy(struct dd_config *ddc, int *ifd, int *ofd) +{ + struct stat st; + int fli = O_RDONLY|O_LARGEFILE|O_NOCTTY, flo = O_WRONLY|O_LARGEFILE|O_NOATIME|O_NOCTTY; + uid_t euid = 0; + + if (ddc->direct) { + fli |= O_DIRECT; + flo |= O_DIRECT; + } + + if (stat(ddc->in, &st) < 0) { + ddc->saved_errno = errno; + return -1; + } + + euid = geteuid(); + + if (!euid || st.st_uid == euid) + fli |= O_NOATIME; + + if ((*ifd = open(ddc->in, fli)) < 0) { + ddc->saved_errno = errno; + return -1; + } + + ddc->fsize = st.st_size; + + /* If "bsize" is not given, use optimum of both FS' */ + if (!ddc->bs) { + struct statfs fst; + memset(&fst, 0, sizeof(fst)); + if (statfs(ddc->out, &fst) < 0 || fst.f_bsize == 0) + fst.f_bsize = 0x1000; + if (fst.f_bsize > st.st_blksize) + ddc->bs = fst.f_bsize; + else + ddc->bs = st.st_blksize; + if (ddc->bs == 0) + ddc->bs = 0x1000; + } + + /* check if device or regular file */ + if (!S_ISREG(st.st_mode)) { + if (S_ISBLK(st.st_mode)) { + if (ioctl(*ifd, BLKGETSIZE64, &ddc->fsize) < 0) { + ddc->saved_errno = errno; + close(*ifd); + return -1; + } + } else { + ddc->fsize = (off_t)-1; + if (ddc->count) + ddc->fsize = ddc->count*ddc->bs; + } + } + + /* skip and seek are in block items */ + ddc->skip *= ddc->bs; + ddc->seek *= ddc->bs; + + /* skip more bytes than are inside source file? */ + if (ddc->fsize != (off_t)-1 && ddc->skip >= (uint64_t)ddc->fsize) { + ddc->saved_errno = EINVAL; + close(*ifd); + return -1; + } + + if (!ddc->seek) + flo |= O_CREAT|O_TRUNC; + + if ((*ofd = open(ddc->out, flo, st.st_mode)) < 0) { + ddc->saved_errno = errno; + close(*ifd); + return -1; + } + + lseek(*ifd, ddc->skip, SEEK_SET); + lseek(*ofd, ddc->seek, SEEK_SET); + posix_fadvise(*ifd, ddc->skip, 0, POSIX_FADV_SEQUENTIAL); + posix_fadvise(*ofd, 0, 0, POSIX_FADV_DONTNEED); + + /* count is in block items too */ + ddc->count *= ddc->bs; + + /* If no count is given, its the filesize minus skip offset */ + if (ddc->count == 0) + ddc->count = ddc->fsize - ddc->skip; + + return 0; +} + +int copy_splice(struct dd_config *ddc) +{ + int ifd, ofd, p[2] = {-1, -1}; + ssize_t r = 0; + size_t n = 0; + if (prepare_copy(ddc, &ifd, &ofd) < 0) + return -1; + if (pipe(p) < 0) { + ddc->saved_errno = errno; + close(ifd); close(ofd); + close(p[0]); close(p[1]); + return -1; + } +#ifdef F_SETPIPE_SZ + for (n = 29; n >= 20; --n) { + if (fcntl(p[0], F_SETPIPE_SZ, 1<<n) != -1) + break; + } +#endif + n = ddc->bs; + for (;ddc->b_out != ddc->count && !sigint;) { + if (n > ddc->count - ddc->b_out) + n = ddc->count - ddc->b_out; + r = splice(ifd, NULL, p[1], NULL, n, SPLICE_F_MORE); + if (r <= 0) { + ddc->saved_errno = errno; + break; + } + ++ddc->rec_in; + r = splice(p[0], NULL, ofd, NULL, r, SPLICE_F_MORE); + if (r <= 0) { + ddc->saved_errno = errno; + break; + } + ddc->b_out += r; + ++ddc->rec_out; + } + close(ifd); + close(ofd); + close(p[0]); + close(p[1]); + if (r < 0) + return -1; + return 0; +} + +static int +copy(struct dd_config *ddc) +{ + int r = 0; + + ddc->t_start = time(NULL); + + r = copy_splice(ddc); + ddc->t_end = time(NULL); + + /* avoid div by zero */ + if (ddc->t_start == ddc->t_end) + ++ddc->t_end; + return r; +} + +static void +usage(void) +{ + fprintf(stderr, "Usage: odd [if=F1] [of=F2] [bsize] [skip=N] [count=N] [quiet] [nosync]\n"); + exit(EXIT_FAILURE); +} + +static void +print_stat(const struct dd_config *ddc) +{ + if (ddc->quiet) + return; + + fprintf(stderr, "%lu records in\n%lu records out\n%lu bytes (%lu MB) copied, %lu s, %f MB/s [%f mB/s]\n", + ddc->rec_in, ddc->rec_out, ddc->b_out, ddc->b_out/(1<<20), + ddc->t_end - ddc->t_start, + ((double)(ddc->b_out/(1<<20)))/(ddc->t_end - ddc->t_start), + ((double)(ddc->b_out/(1000*1000)))/(ddc->t_end - ddc->t_start)); +} + +static void +sig_int(int unused) +{ + (void) unused; + fprintf(stderr, "SIGINT! Aborting ...\n"); + sigint = 1; +} + +int +main(int argc, char *argv[]) +{ + int i = 0; + char buf[1024]; + struct dd_config config; + + memset(&config, 0, sizeof(config)); + config.bs = 1<<16; + config.in = "/dev/stdin"; + config.out = "/dev/stdout"; + + /* emulate 'dd' argument parsing */ + for (i = 1; i < argc; ++i) { + memset(buf, 0, sizeof(buf)); + if (sscanf(argv[i], "if=%1023c", buf) == 1) + config.in = strdup(buf); + else if (sscanf(argv[i], "of=%1023c", buf) == 1) + config.out = strdup(buf); + else if (sscanf(argv[i], "skip=%1023c", buf) == 1) + config.skip = strtoul(buf, NULL, 10); + else if (sscanf(argv[i], "seek=%1023c", buf) == 1) + config.seek = strtoul(buf, NULL, 10); + else if (sscanf(argv[i], "count=%1023c", buf) == 1) + config.count = strtoul(buf, NULL, 10); + else if (strcmp(argv[i], "direct") == 0) + config.direct = 1; + else if (sscanf(argv[i], "bs=%1023c", buf) == 1) + config.bs = strtoul(buf, NULL, 10); + else if (strcmp(argv[i], "bs") == 0) + config.bs = 0; + else if (strcmp(argv[i], "quiet") == 0) + config.quiet = 1; + else if (strcmp(argv[i], "nosync") == 0) + config.nosync = 1; + } + + if (!config.in || !config.out) + usage(); + + signal(SIGPIPE, SIG_IGN); + signal(SIGINT, sig_int); + + if (copy(&config) < 0) + fprintf(stderr, "Error: %s\n", strerror(config.saved_errno)); + print_stat(&config); + + if (config.nosync == 0) + sync(); sync(); + return EXIT_SUCCESS; +}