|  | // Copyright 2017 The Fuchsia Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include <dirent.h> | 
|  | #include <errno.h> | 
|  | #include <fcntl.h> | 
|  | #include <limits.h> | 
|  | #include <stdarg.h> | 
|  | #include <stdbool.h> | 
|  | #include <stdio.h> | 
|  | #include <stdlib.h> | 
|  | #include <string.h> | 
|  | #include <sys/stat.h> | 
|  | #include <unistd.h> | 
|  |  | 
|  | int usage(void) { | 
|  | fprintf(stderr, "usage: dd [OPTIONS]\n"); | 
|  | fprintf(stderr, "dd can be used to convert and copy files\n"); | 
|  | fprintf(stderr, " bs=BYTES  : read and write BYTES bytes at a time\n"); | 
|  | fprintf(stderr, " count=N   : copy only N input blocks\n"); | 
|  | fprintf(stderr, " ibs=BYTES : read BYTES bytes at a time (default: 512)\n"); | 
|  | fprintf(stderr, " if=FILE   : read from FILE instead of stdin\n"); | 
|  | fprintf(stderr, " obs=BYTES : write BYTES bytes at a time (default: 512)\n"); | 
|  | fprintf(stderr, " of=FILE   : write to FILE instead of stdout\n"); | 
|  | fprintf(stderr, " seek=N    : skip N obs-sized blocks at start of output\n"); | 
|  | fprintf(stderr, " skip=N    : skip N ibs-sized blocks at start of input\n"); | 
|  | fprintf(stderr, " N and BYTES may be followed by the following multiplicitive\n" | 
|  | " suffixes: c = 1, w = 2, b = 512, kB = 1000, K = 1024,\n" | 
|  | "           MB = 1000 * 1000, M = 1024 * 1024, xM = M,\n" | 
|  | "           GB = 1000 * 1000 * 1000, GB = 1024 * 1024 * 1024\n"); | 
|  | fprintf(stderr, " --help : Show this help message\n"); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | // Returns "true" if the argument matches the prefix. | 
|  | // In this case, moves the argument past the prefix. | 
|  | bool prefix_match(const char** arg, const char* prefix) { | 
|  | if (!strncmp(*arg, prefix, strlen(prefix))) { | 
|  | *arg += strlen(prefix); | 
|  | return true; | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | #define MAYBE_MULTIPLY_SUFFIX(str, out, suffix, value) \ | 
|  | if (!strcmp((str), (suffix))) {                \ | 
|  | (out) *= (value);                          \ | 
|  | return 0;                                  \ | 
|  | } | 
|  |  | 
|  | // Parse the formatted size string from |s|, and place | 
|  | // the result in |out|. | 
|  | // | 
|  | // Returns 0 on success. | 
|  | int parse_size(const char* s, size_t* out) { | 
|  | char* endptr; | 
|  | if (!(*s >= '0' && *s <= '9')) { | 
|  | goto done; | 
|  | } | 
|  | *out = strtol(s, &endptr, 10); | 
|  | if (*endptr == '\0') { | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | MAYBE_MULTIPLY_SUFFIX(endptr, *out, "G", 1UL << 30); | 
|  | MAYBE_MULTIPLY_SUFFIX(endptr, *out, "GB", 1000 * 1000 * 1000UL); | 
|  | MAYBE_MULTIPLY_SUFFIX(endptr, *out, "xM", 1UL << 20); | 
|  | MAYBE_MULTIPLY_SUFFIX(endptr, *out, "M", 1UL << 20); | 
|  | MAYBE_MULTIPLY_SUFFIX(endptr, *out, "MB", 1000 * 1000UL); | 
|  | MAYBE_MULTIPLY_SUFFIX(endptr, *out, "K", 1UL << 10); | 
|  | MAYBE_MULTIPLY_SUFFIX(endptr, *out, "kB", 1000UL); | 
|  | MAYBE_MULTIPLY_SUFFIX(endptr, *out, "b", 512UL); | 
|  | MAYBE_MULTIPLY_SUFFIX(endptr, *out, "w", 2UL); | 
|  | MAYBE_MULTIPLY_SUFFIX(endptr, *out, "c", 1UL); | 
|  |  | 
|  | done: | 
|  | fprintf(stderr, "Couldn't parse size string: %s\n", s); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | typedef struct { | 
|  | bool use_count; | 
|  | size_t count; | 
|  | size_t input_bs; | 
|  | size_t input_skip; | 
|  | size_t output_bs; | 
|  | size_t output_seek; | 
|  | char input[PATH_MAX]; | 
|  | char output[PATH_MAX]; | 
|  | } dd_options_t; | 
|  |  | 
|  | int parse_args(int argc, const char** argv, dd_options_t* options) { | 
|  | while (argc > 1) { | 
|  | const char* arg = argv[1]; | 
|  | if (prefix_match(&arg, "bs=")) { | 
|  | size_t size; | 
|  | if (parse_size(arg, &size)) { | 
|  | return usage(); | 
|  | } | 
|  | options->input_bs = size; | 
|  | options->output_bs = size; | 
|  | } else if (prefix_match(&arg, "count=")) { | 
|  | if (parse_size(arg, &options->count)) { | 
|  | return usage(); | 
|  | } | 
|  | options->use_count = true; | 
|  | } else if (prefix_match(&arg, "ibs=")) { | 
|  | if (parse_size(arg, &options->input_bs)) { | 
|  | return usage(); | 
|  | } | 
|  | } else if (prefix_match(&arg, "obs=")) { | 
|  | if (parse_size(arg, &options->output_bs)) { | 
|  | return usage(); | 
|  | } | 
|  | } else if (prefix_match(&arg, "seek=")) { | 
|  | if (parse_size(arg, &options->output_seek)) { | 
|  | return usage(); | 
|  | } | 
|  | } else if (prefix_match(&arg, "skip=")) { | 
|  | if (parse_size(arg, &options->input_skip)) { | 
|  | return usage(); | 
|  | } | 
|  | } else if (prefix_match(&arg, "if=")) { | 
|  | strncpy(options->input, arg, PATH_MAX); | 
|  | options->input[PATH_MAX - 1] = '\0'; | 
|  | } else if (prefix_match(&arg, "of=")) { | 
|  | strncpy(options->output, arg, PATH_MAX); | 
|  | options->output[PATH_MAX - 1] = '\0'; | 
|  | } else { | 
|  | return usage(); | 
|  | } | 
|  | argc--; | 
|  | argv++; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | #define MIN(x,y) ((x) < (y) ? (x) : (y)) | 
|  | #define MAX(x,y) ((x) < (y) ? (y) : (x)) | 
|  |  | 
|  | int main(int argc, const char** argv) { | 
|  | dd_options_t options; | 
|  | memset(&options, 0, sizeof(dd_options_t)); | 
|  | options.input_bs = 512; | 
|  | options.output_bs = 512; | 
|  | int r; | 
|  | if ((r = parse_args(argc, argv, &options))) { | 
|  | return r; | 
|  | } | 
|  |  | 
|  | if (options.input_bs == 0 || options.output_bs == 0) { | 
|  | fprintf(stderr, "block sizes must be greater than zero\n"); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | options.input_skip *= options.input_bs; | 
|  | options.output_seek *= options.output_bs; | 
|  |  | 
|  | // Input and output fds | 
|  | int in = -1; | 
|  | int out = -1; | 
|  | // Buffer to contain partially read data | 
|  | char* buf = NULL; | 
|  | // Return code | 
|  | r = -1; | 
|  | // Number of full records copied to/from target | 
|  | size_t records_in = 0; | 
|  | size_t records_out = 0; | 
|  | // Size of remaining "partial" transfer from input / to output. | 
|  | size_t record_in_partial = 0; | 
|  | size_t record_out_partial = 0; | 
|  |  | 
|  | if (*options.input == '\0') { | 
|  | in = STDIN_FILENO; | 
|  | } else { | 
|  | in = open(options.input, O_RDONLY); | 
|  | if (in < 0) { | 
|  | fprintf(stderr, "Couldn't open input file %s : %d\n", options.input, errno); | 
|  | goto done; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (*options.output == '\0') { | 
|  | out = STDOUT_FILENO; | 
|  | } else { | 
|  | out = open(options.output, O_WRONLY | O_CREAT); | 
|  | if (out < 0) { | 
|  | fprintf(stderr, "Couldn't open output file %s : %d\n", options.output, errno); | 
|  | goto done; | 
|  | } | 
|  | } | 
|  |  | 
|  | buf = malloc(MAX(options.output_bs, options.input_bs)); | 
|  | if (buf == NULL) { | 
|  | fprintf(stderr, "No memory\n"); | 
|  | goto done; | 
|  | } | 
|  |  | 
|  | if (options.input_skip != 0) { | 
|  | // Try seeking first; if that doesn't work, try reading to an input buffer. | 
|  | if (lseek(in, options.input_skip, SEEK_SET) != (off_t) options.input_skip) { | 
|  | while (options.input_skip) { | 
|  | if (read(in, buf, options.input_bs) != (ssize_t) options.input_bs) { | 
|  | fprintf(stderr, "Couldn't read from input\n"); | 
|  | goto done; | 
|  | } | 
|  | options.input_skip -= options.input_bs; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | if (options.output_seek != 0) { | 
|  | if (lseek(out, options.output_seek, SEEK_SET) != (off_t) options.output_seek) { | 
|  | fprintf(stderr, "Failed to seek on output\n"); | 
|  | goto done; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (MAX(options.input_bs, options.output_bs) % | 
|  | MIN(options.input_bs, options.output_bs) != 0) { | 
|  | // TODO(smklein): Implement this case, rather than returning an error | 
|  | fprintf(stderr, "Input and output block sizes must be multiples\n"); | 
|  | goto done; | 
|  | } | 
|  |  | 
|  | bool terminating = false; | 
|  | size_t rlen = 0; | 
|  | while (true) { | 
|  | if (options.use_count && !options.count) { | 
|  | r = 0; | 
|  | goto done; | 
|  | } | 
|  |  | 
|  | // Read as much as we can (up to input_bs) into our target buffer | 
|  | ssize_t rout; | 
|  | if ((rout = read(in, buf, options.input_bs)) != (ssize_t) options.input_bs) { | 
|  | terminating = true; | 
|  | } | 
|  | if (rout == (ssize_t) options.input_bs) { | 
|  | records_in++; | 
|  | } else if (rout > 0) { | 
|  | record_in_partial = rout; | 
|  | } | 
|  | if (rout > 0) { | 
|  | rlen += rout; | 
|  | } | 
|  | if (options.use_count) { | 
|  | --options.count; | 
|  | if (options.count == 0) { | 
|  | terminating = true; | 
|  | } | 
|  | } | 
|  |  | 
|  | // If we can (or should, due to impending termination), dump the read | 
|  | // buffer into the output file. | 
|  | if (rlen >= options.output_bs || terminating) { | 
|  | size_t off = 0; | 
|  | while (off != rlen) { | 
|  | size_t wlen = MIN(options.output_bs, rlen - off); | 
|  | if (write(out, buf + off, wlen) != (ssize_t) wlen) { | 
|  | fprintf(stderr, "Couldn't write %zu bytes to output\n", wlen); | 
|  | goto done; | 
|  | } | 
|  | if (wlen == options.output_bs) { | 
|  | records_out++; | 
|  | } else { | 
|  | record_out_partial = wlen; | 
|  | } | 
|  | off += wlen; | 
|  | } | 
|  | rlen = 0; | 
|  | } | 
|  |  | 
|  | if (terminating) { | 
|  | r = 0; | 
|  | goto done; | 
|  | } | 
|  | } | 
|  |  | 
|  | done: | 
|  | printf("%zu+%u records in\n", records_in, record_in_partial ? 1 : 0); | 
|  | printf("%zu+%u records out\n", records_out, record_out_partial ? 1 : 0); | 
|  | printf("%zu bytes copied\n", records_out * options.output_bs + record_out_partial); | 
|  |  | 
|  | if (in != -1) { | 
|  | close(in); | 
|  | } | 
|  | if (out != -1) { | 
|  | close(out); | 
|  | } | 
|  | free(buf); | 
|  | return r; | 
|  | } |