#include <stdio.h>
#include <unistd.h>
#include <getopt.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include "common.h"
#include "output.h"
#include "mmap.h"
#include "file.h"
#include "util.h"
#include "os.h"
struct ddmap_context {
char *file_in;
char *file_out;
char *str;
os_off_t offset_in;
os_off_t offset_out;
size_t len;
int checksum;
};
static struct ddmap_context ddmap_default;
static void
print_usage(void)
{
printf("Usage: ddmap [option] ...\n");
printf("Valid options:\n");
printf("-i FILE - read from FILE\n");
printf("-o FILE - write to FILE\n");
printf("-d STRING - STRING to be written\n");
printf("-s N - skip N bytes at start of input\n");
printf("-q N - skip N bytes at start of output\n");
printf("-l N - read or write up to N bytes at a time\n");
printf("-c - compute checksum\n");
printf("-h - print this usage info\n");
}
static const struct option long_options[] = {
{"input-file", required_argument, NULL, 'i'},
{"output-file", required_argument, NULL, 'o'},
{"string", required_argument, NULL, 'd'},
{"offset-in", required_argument, NULL, 's'},
{"offset-out", required_argument, NULL, 'q'},
{"length", required_argument, NULL, 'l'},
{"checksum", no_argument, NULL, 'c'},
{"help", no_argument, NULL, 'h'},
{NULL, 0, NULL, 0 },
};
static void
ddmap_print_bytes(const char *data, size_t len)
{
for (size_t i = 0; i < len; ++i) {
if (data[i] == '\0')
printf("\u00B0");
else if (data[i] >= ' ' && data[i] <= '~')
printf("%c", data[i]);
else
printf("\u00B7");
}
printf("\n");
}
static int
ddmap_read(const char *path, os_off_t offset, size_t len)
{
char *read_buff = Zalloc(len + 1);
if (read_buff == NULL) {
outv_err("Zalloc(%zu) failed\n", len + 1);
return -1;
}
ssize_t read_len = util_file_pread(path, read_buff, len, offset);
if (read_len < 0) {
outv_err("pread failed");
Free(read_buff);
return -1;
} else if ((size_t)read_len < len) {
outv(1, "read less bytes than requested: %zd vs. %zu\n",
read_len, len);
}
ddmap_print_bytes(read_buff, (size_t)read_len);
Free(read_buff);
return 0;
}
static int
ddmap_zero(const char *path, size_t offset, size_t len)
{
void *addr;
ssize_t filesize = util_file_get_size(path);
if (filesize < 0) {
outv_err("invalid file size");
return -1;
}
if (offset + len > (size_t)filesize)
len = (size_t)filesize - offset;
addr = util_file_map_whole(path);
if (addr == NULL) {
outv_err("map failed");
return -1;
}
memset((char *)addr + offset, 0, len);
util_unmap(addr, (size_t)filesize);
return 0;
}
static int
ddmap_write_data(const char *path, const char *data,
os_off_t offset, size_t len)
{
if (util_file_pwrite(path, data, len, offset) < 0) {
outv_err("pwrite for dax device failed: path %s,"
" len %zu, offset %zd", path, len, offset);
return -1;
}
return 0;
}
static int
ddmap_write_from_file(const char *path_in, const char *path_out,
os_off_t offset_in, os_off_t offset_out, size_t len)
{
char *src;
ssize_t file_in_size = util_file_get_size(path_in);
if ((size_t)file_in_size < len + (size_t)offset_in) {
outv_err("offset with length exceed input file size");
return -1;
}
util_init();
src = util_file_map_whole(path_in);
src += offset_in;
ddmap_write_data(path_out, src, offset_out, len);
util_unmap(src, (size_t)file_in_size);
return 0;
}
static int
ddmap_write(const char *path, const char *str, os_off_t offset, size_t len)
{
size_t length;
size_t str_len = (str != NULL) ? strlen(str) + 1 : 0;
if (len == 0)
length = str_len;
else
length = min(len, str_len);
if (length > 0) {
if (ddmap_write_data(path, str, offset, length))
return -1;
}
if (length < len) {
if (ddmap_zero(path, (size_t)offset + length, len - length))
return -1;
}
return 0;
}
static int
ddmap_checksum(const char *path, size_t len, os_off_t offset)
{
char *src;
uint64_t checksum;
ssize_t filesize = util_file_get_size(path);
if ((size_t)filesize < len + (size_t)offset) {
outv_err("offset with length exceed file size");
return -1;
}
util_init();
src = util_file_map_whole(path);
util_checksum(src + offset, len, &checksum, 1);
util_unmap(src, (size_t)filesize);
printf("%" PRIu64 "\n", checksum);
return 0;
}
static int
parse_args(struct ddmap_context *ctx, int argc, char *argv[])
{
int opt;
char *endptr;
os_off_t offset;
size_t length;
while ((opt = getopt_long(argc, argv, "i:o:d:s:q:l:chv",
long_options, NULL)) != -1) {
switch (opt) {
case 'i':
ctx->file_in = optarg;
break;
case 'o':
ctx->file_out = optarg;
break;
case 'd':
ctx->str = optarg;
break;
case 's':
offset = strtol(optarg, &endptr, 0);
if ((endptr && *endptr != '\0') || errno) {
outv_err("'%s' -- invalid input offset",
optarg);
return -1;
}
ctx->offset_in = offset;
break;
case 'q':
offset = strtol(optarg, &endptr, 0);
if ((endptr && *endptr != '\0') || errno) {
outv_err("'%s' -- invalid output offset",
optarg);
return -1;
}
ctx->offset_out = offset;
break;
case 'l':
length = strtoul(optarg, &endptr, 0);
if ((endptr && *endptr != '\0') || errno) {
outv_err("'%s' -- invalid length", optarg);
return -1;
}
ctx->len = length;
break;
case 'c':
ctx->checksum = 1;
break;
case 'h':
print_usage();
exit(EXIT_SUCCESS);
case 'v':
out_set_vlevel(1);
break;
default:
print_usage();
exit(EXIT_FAILURE);
}
}
return 0;
}
static int
validate_args(struct ddmap_context *ctx)
{
if ((ctx->file_in == NULL) && (ctx->file_out == NULL)) {
outv_err("an input file and/or an output file must be "
"provided");
return -1;
} else if (ctx->file_out == NULL) {
if (ctx->len == 0) {
outv_err("number of bytes to read has to be provided");
return -1;
}
} else if (ctx->file_in == NULL) {
if (ctx->str == NULL && ctx->len == 0) {
outv_err("when writing, 'data' or 'length' option has"
" to be provided");
return -1;
}
}
return 0;
}
static int
do_ddmap(struct ddmap_context *ctx)
{
if ((ctx->file_in != NULL) && (ctx->file_out != NULL)) {
if (ddmap_write_from_file(ctx->file_in, ctx->file_out,
ctx->offset_in, ctx->offset_out, ctx->len))
return -1;
return 0;
}
if ((ctx->checksum == 1) && (ctx->file_in != NULL)) {
if (ddmap_checksum(ctx->file_in, ctx->len, ctx->offset_in))
return -1;
return 0;
}
if (ctx->file_in != NULL) {
if (ddmap_read(ctx->file_in, ctx->offset_in, ctx->len))
return -1;
} else {
if (ddmap_write(ctx->file_out, ctx->str, ctx->offset_in,
ctx->len))
return -1;
}
return 0;
}
int
main(int argc, char *argv[])
{
#ifdef _WIN32
wchar_t **wargv = CommandLineToArgvW(GetCommandLineW(), &argc);
for (int i = 0; i < argc; i++) {
argv[i] = util_toUTF8(wargv[i]);
if (argv[i] == NULL) {
for (i--; i >= 0; i--)
free(argv[i]);
outv_err("Error during arguments conversion\n");
return 1;
}
}
#endif
int ret = 0;
struct ddmap_context ctx = ddmap_default;
if ((ret = parse_args(&ctx, argc, argv)))
goto out;
if ((ret = validate_args(&ctx)))
goto out;
if ((ret = do_ddmap(&ctx))) {
outv_err("failed to perform ddmap\n");
if (errno)
outv_err("errno: %s\n", strerror(errno));
ret = -1;
goto out;
}
out:
#ifdef _WIN32
for (int i = argc; i > 0; i--)
free(argv[i - 1]);
#endif
return ret;
}