From 77de1f5ac06177ef75b941018cc6fa1d88dc73f7 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Wed, 4 Feb 2026 22:59:39 +0100 Subject: [PATCH] Initial --- .gitignore | 4 ++ Makefile | 22 ++++++ README.txt | 6 ++ writeimg.c | 195 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 227 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 README.txt create mode 100644 writeimg.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0e63c46 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.cache +compile_commands.json +*.o +writeimg diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..266780b --- /dev/null +++ b/Makefile @@ -0,0 +1,22 @@ +GITREV ?= $(shell git describe --dirty --always) +BLDDATE ?= $(shell date -I) +CR_YEAR ?= $(shell date +%Y) + +CFLAGS ?= -Wall -Wextra -Wpedantic -O2 -std=gnu99 +CFLAGS += -DGITREV='"$(GITREV)"' +CFLAGS += -DBLDDATE='"$(BLDDATE)"' +CFLAGS += -DCR_YEAR='"$(CR_YEAR)"' + +# Soon... +# CFLAGS += $(shell pkg-config --cflags libudev) +# LIBS += $(shell pkg-config --libs libudev) + +writeimg: writeimg.c + $(CC) $(CFLAGS) $(LIBS) -o $@ $^ + +clean: + rm -f *.o writeimg + +install: writeimg + install -c -s writeimg /usr/local/bin/writeimg + install -m 0755 $(PROG) $(DESTDIR)$(BINDIR)/$(PROG) diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..7c6c7ae --- /dev/null +++ b/README.txt @@ -0,0 +1,6 @@ +Simple dd-like image writer with additional security checks. + +dd if=/dev/zero of=./disk.img bs=1M count=1024 +losetup -fP ./disk.img +losetup -a +losedup -d /dev/loop0 diff --git a/writeimg.c b/writeimg.c new file mode 100644 index 0000000..4877040 --- /dev/null +++ b/writeimg.c @@ -0,0 +1,195 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef GITREV +#define VERSION GITREV +#else +#define VERSION "1.0.0" +#endif + +// clang-format off +const char help[] = + "Usage:\n" + " writeimg [-v] -d \n" + "\n" + "Args:\n" + " Binary image file\n" + " -v Verify only\n" + " -d device Target block device\n" + " -h, --help Print this help message\n" + " -V, --version Print version\n" + "\0"; +// clang-format on + +const char copyright[] = "Copyright (C) %s Imbus\n"; + +#define BUFSIZE (4 * 1024 * 1024) + +// char *buffer = NULL; + +typedef struct write_job { + char *filename; + char *dev_name; + char *buffer; + char *buffer2; /* For memcmp integrity checks */ + size_t bufsize; + size_t block_size; + char verify_only; +} write_job_t; + +write_job_t wjob = {0}; + +void int_handler(int signum) { + fprintf(stderr, "\nInterrupted: %s\n", strsignal(signum)); + + if (wjob.buffer) + free(wjob.buffer); + + if (wjob.buffer2) + free(wjob.buffer2); + + exit(EXIT_FAILURE); +} + +int perform_write(write_job_t *job) { + int block_fd = open(job->dev_name, O_RDWR); + int file_fd = open(job->filename, O_RDONLY); + + /* TODO: Checks */ + assert(job->bufsize >= job->block_size); + + while (!job->verify_only) { + ssize_t read_bytes = read(file_fd, job->buffer, job->block_size); + if (read_bytes == 0) { + fsync(block_fd); + break; /* Finished */ + } + + if (read_bytes < 0) { + fprintf(stderr, "%s: Read error\n", job->filename); + perror("Read"); + exit(EXIT_FAILURE); + } + + ssize_t written_bytes = write(block_fd, job->buffer, read_bytes); + if (written_bytes < 0) { + fprintf(stderr, "%s: Write error\n", job->dev_name); + perror("Write"); + exit(EXIT_FAILURE); + } + } + + lseek(block_fd, 0, SEEK_SET); + lseek(file_fd, 0, SEEK_SET); + + memset(job->buffer, 0, BUFSIZE); + memset(job->buffer2, 0, BUFSIZE); + + while (1) { + ssize_t read_file = read(file_fd, job->buffer2, job->block_size); + ssize_t read_block = read(block_fd, job->buffer, job->block_size); + + assert(read_block >= read_file); + assert(read_block >= 0); + assert(read_file >= 0); + + if (read_file == 0) + break; + + int cmp = memcmp((const void *)job->buffer2, (const void *)job->buffer, (size_t)read_file); + if (0 != cmp) { + fprintf(stderr, "WARNING: File did not verify correctly!\n"); + exit(EXIT_FAILURE); + } + } + + close(block_fd); + close(file_fd); + return 1; +} + +static const struct option longopts[] = { + {"help", 0, 0, 'h'}, + {"version", 0, 0, 'V'}, + {"device", 1, 0, 'd'}, + {NULL, 0, 0, 0}, +}; + +int main(int argc, char *argv[]) { + printf("%s version %s\n", basename(argv[0]), VERSION); + printf("Build date: %s\n", BLDDATE); + fprintf(stdout, copyright, CR_YEAR); + printf("\n"); + + /* Line buffering, system allocated */ + setvbuf(stdout, NULL, _IOLBF, 0); + setvbuf(stderr, NULL, _IOLBF, 0); + + /* Hang these up to alert the user that we were interrupted */ + signal(SIGINT, int_handler); + signal(SIGHUP, int_handler); + signal(SIGTERM, int_handler); + + int c = {0}; + while ((c = getopt_long(argc, argv, "vd:hV", longopts, 0)) != -1) { + switch (c) { + case 'v': ++wjob.verify_only; continue; + case 'd': wjob.dev_name = optarg; continue; + case 'h': break; + case 'V': exit(EXIT_SUCCESS); + } + printf("%s\n", help); + exit(EXIT_SUCCESS); + } + + argc -= optind; + argv += optind; + + /* argv[0] should now be the image file*/ + + if (argc != 1) { + printf("You need to specify an image file.\n"); + // printf("%s\n", help); + exit(EXIT_FAILURE); + } + + wjob.filename = argv[0]; + struct stat file_stat = {0}; + if (0 != stat(wjob.filename, &file_stat)) { + printf("File does not exist...\n"); + exit(EXIT_FAILURE); + } + + if (NULL == wjob.dev_name) { + printf("Device required...\n"); + exit(EXIT_FAILURE); + } + + printf("Writing %s to %s\n", wjob.filename, wjob.dev_name); + + wjob.buffer = malloc(BUFSIZE); + wjob.buffer2 = malloc(BUFSIZE); + + wjob.bufsize = BUFSIZE; + wjob.block_size = BUFSIZE; + + perform_write(&wjob); + + if (wjob.buffer) + free(wjob.buffer); + if (wjob.buffer2) + free(wjob.buffer2); + + printf("\nOkay!\n"); + exit(0); + return 0; +}