paw: host: Add CUMHOLE pre-boot image format
Signed-off-by: Chloe M. <chloe@mensia.org>
This commit is contained in:
@@ -0,0 +1,29 @@
|
||||
#
|
||||
# Copyright (c) 2026, Chloe M.
|
||||
# Provided under the BSD-3 clause
|
||||
#
|
||||
# Description: CUMHOLE build script
|
||||
# Author: Chloe M.
|
||||
#
|
||||
|
||||
.SILENT:
|
||||
PROMPT := printf "%s\t\t%s\n"
|
||||
|
||||
CC = gcc
|
||||
CFILES = $(shell find . -name "*.c")
|
||||
OFILES = $(CFILES:.c=.o)
|
||||
|
||||
CFLAGS = \
|
||||
-Wall \
|
||||
-pedantic
|
||||
|
||||
.PHONY: all
|
||||
all: ../bin/hole
|
||||
|
||||
.PHONY: hole
|
||||
../bin/hole: $(OFILES)
|
||||
$(CC) $^ -o $@
|
||||
|
||||
%.o: %.c
|
||||
$(PROMPT) "CC" $<
|
||||
$(CC) -c $(CFLAGS) $< -o $@
|
||||
@@ -0,0 +1,347 @@
|
||||
/*
|
||||
* Copyright (c) 2026, Chloe M.
|
||||
* Provided under the BSD-3 clause.
|
||||
*
|
||||
* Description: CUMHOLE pre-boot image format
|
||||
* Author: Chloe M.
|
||||
*/
|
||||
|
||||
#define _DEFAULT_SOURCE
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/queue.h>
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <dirent.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define REF_MAGIC "CUMHOLE"
|
||||
#define REF_MAGIC_LEN 8
|
||||
#define REF_PATH_LEN 256
|
||||
|
||||
/* Set to 1 for verbose */
|
||||
#define DEBUG 0
|
||||
|
||||
#if DEBUG
|
||||
#define DTRACE(fmt, ...) printf(fmt, ##__VA_ARGS__)
|
||||
#else
|
||||
#define DTRACE(...) (void)0
|
||||
#endif
|
||||
|
||||
/*
|
||||
* A reference is an entry within the table that refers
|
||||
* to a block of data after the reference table.
|
||||
*
|
||||
* @magic: Reference magic
|
||||
* @path: File path
|
||||
* @size: File size
|
||||
* @data_off: Data offset
|
||||
* @real_path: Real path
|
||||
* @link: Queue link
|
||||
*
|
||||
* XXX: Do not reorder anything ABOVE 'link'
|
||||
*/
|
||||
struct hole_ref {
|
||||
char magic[REF_MAGIC_LEN];
|
||||
char path[REF_PATH_LEN];
|
||||
size_t size;
|
||||
off_t data_off;
|
||||
char *real_path;
|
||||
TAILQ_ENTRY(hole_ref) link;
|
||||
};
|
||||
|
||||
/* Globals */
|
||||
static bool output_override = false;
|
||||
static char *output_path = "out.hole";
|
||||
static char *fill_dirpath = NULL;
|
||||
static size_t file_count = 0;
|
||||
static TAILQ_HEAD(, hole_ref) hole_files;
|
||||
|
||||
/*
|
||||
* Display the help options
|
||||
*/
|
||||
static void
|
||||
help(void)
|
||||
{
|
||||
printf("usage: ./hole [flags]\n");
|
||||
printf("[-h] Display this help menu\n");
|
||||
printf("[-f] Fill hole with directory\n");
|
||||
printf("[-o] Output blob path\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Destroy the hole files table
|
||||
*/
|
||||
static void
|
||||
hole_files_destroy(void)
|
||||
{
|
||||
struct hole_ref *ref, *tmp;
|
||||
|
||||
while ((ref = TAILQ_FIRST(&hole_files)) != NULL) {
|
||||
DTRACE("delete %s\n", ref->real_path);
|
||||
free(ref->real_path);
|
||||
|
||||
tmp = ref;
|
||||
TAILQ_REMOVE(&hole_files, ref, link);
|
||||
free(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Used to fix up offsets after
|
||||
*/
|
||||
static void
|
||||
hole_table_fixup(void)
|
||||
{
|
||||
struct stat sb;
|
||||
struct hole_ref *ref;
|
||||
size_t table_end, offset;
|
||||
size_t hdr_size;
|
||||
|
||||
hdr_size = sizeof(struct hole_ref);
|
||||
hdr_size -= sizeof(ref->real_path);
|
||||
hdr_size -= sizeof(ref->link);
|
||||
table_end = file_count * hdr_size;
|
||||
offset = table_end;
|
||||
|
||||
printf("* fixing up offsets...\n");
|
||||
printf("* table end @ %zx\n", table_end);
|
||||
|
||||
TAILQ_FOREACH(ref, &hole_files, link) {
|
||||
if (stat(ref->real_path, &sb) < 0) {
|
||||
perror("stat");
|
||||
continue;
|
||||
}
|
||||
|
||||
ref->size = sb.st_size;
|
||||
ref->data_off = offset;
|
||||
|
||||
DTRACE("data_off=%zx\n", ref->data_off);
|
||||
offset += ref->size;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
blob_write(void)
|
||||
{
|
||||
struct hole_ref *ref;
|
||||
int fd, ffd;
|
||||
void *data;
|
||||
size_t fsize, hdr_len;
|
||||
|
||||
fd = open(output_path, O_RDWR | O_CREAT | O_TRUNC, 0777);
|
||||
if (fd < 0) {
|
||||
perror("open");
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* PASS 1):
|
||||
* We are to write the header of each file into the
|
||||
* top of the blob.
|
||||
*/
|
||||
TAILQ_FOREACH(ref, &hole_files, link) {
|
||||
hdr_len = sizeof(*ref);
|
||||
hdr_len -= sizeof(ref->real_path);
|
||||
hdr_len -= sizeof(ref->link);
|
||||
write(fd, ref, hdr_len);
|
||||
}
|
||||
|
||||
/*
|
||||
* PASS 2):
|
||||
* Now we can write the contents of each file after
|
||||
*/
|
||||
TAILQ_FOREACH(ref, &hole_files, link) {
|
||||
ffd = open(ref->real_path, O_RDONLY);
|
||||
if (ffd < 0) {
|
||||
perror("open[ffd]");
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Obtain the file size */
|
||||
fsize = lseek(ffd, 0, SEEK_END);
|
||||
lseek(ffd, 0, SEEK_SET);
|
||||
|
||||
data = mmap(
|
||||
NULL,
|
||||
fsize,
|
||||
PROT_READ,
|
||||
MAP_SHARED,
|
||||
ffd,
|
||||
0
|
||||
);
|
||||
|
||||
if (data == NULL) {
|
||||
close(ffd);
|
||||
perror("mmap");
|
||||
continue;
|
||||
}
|
||||
|
||||
lseek(fd, SEEK_SET, ref->data_off);
|
||||
write(fd, data, fsize);
|
||||
munmap(data, fsize);
|
||||
close(ffd);
|
||||
}
|
||||
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Strip a path of leading '.' and '/' characters
|
||||
*
|
||||
* @path: Path to strip
|
||||
*/
|
||||
static const char *
|
||||
path_strip(const char *path)
|
||||
{
|
||||
const char *p;
|
||||
|
||||
if (path == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
p = path;
|
||||
while (*p == '.' || *p == '/') {
|
||||
++p;
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
/*
|
||||
* Push a file to the hole files table
|
||||
*/
|
||||
static int
|
||||
push_file(const char *path)
|
||||
{
|
||||
struct hole_ref *ref;
|
||||
const char *stripped;
|
||||
|
||||
if (path == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ref = malloc(sizeof(*ref));
|
||||
if (ref == NULL) {
|
||||
printf("fatal: could not allocate ref\n");
|
||||
hole_files_destroy();
|
||||
return -1;
|
||||
}
|
||||
|
||||
ref->real_path = strdup(path);
|
||||
stripped = path_strip(path);
|
||||
|
||||
memcpy(ref->magic, REF_MAGIC, sizeof(REF_MAGIC));
|
||||
memcpy(ref->path, stripped, strlen(stripped) + 1);
|
||||
ref->data_off = 0;
|
||||
|
||||
TAILQ_INSERT_TAIL(&hole_files, ref, link);
|
||||
++file_count;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Scan a directory to fill the blob
|
||||
*/
|
||||
static void
|
||||
scan_dir(const char *path)
|
||||
{
|
||||
DIR *dir;
|
||||
struct dirent *dirent;
|
||||
char pathbuf[300];
|
||||
|
||||
if (path == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
dir = opendir(path);
|
||||
if (dir == NULL) {
|
||||
perror("opendir");
|
||||
return;
|
||||
}
|
||||
|
||||
while ((dirent = readdir(dir)) != NULL) {
|
||||
if (dirent->d_name[0] == '.') {
|
||||
continue;
|
||||
}
|
||||
|
||||
snprintf(pathbuf, sizeof(pathbuf), "%s/%s", path, dirent->d_name);
|
||||
switch (dirent->d_type) {
|
||||
case DT_REG:
|
||||
printf("[f] %s\n", pathbuf);
|
||||
if (push_file(pathbuf) < 0) {
|
||||
free(fill_dirpath);
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
case DT_DIR:
|
||||
printf("[d] %s\n", pathbuf);
|
||||
scan_dir(pathbuf);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
int opt;
|
||||
|
||||
if (argc < 2) {
|
||||
printf("fatal: too few arguments\n");
|
||||
help();
|
||||
return -1;
|
||||
}
|
||||
|
||||
while ((opt = getopt(argc, argv, "hf:o:")) != -1) {
|
||||
switch (opt) {
|
||||
case 'h':
|
||||
help();
|
||||
return -1;
|
||||
case 'f':
|
||||
fill_dirpath = strdup(optarg);
|
||||
if (fill_dirpath == NULL) {
|
||||
printf("fatal: out of memory\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
break;
|
||||
case 'o':
|
||||
output_override = true;
|
||||
output_path = strdup(optarg);
|
||||
if (output_path == NULL) {
|
||||
printf("fatal: out of memory\n");
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (fill_dirpath == NULL) {
|
||||
printf("fatal: expected fill directory for the slutty blob...\n");
|
||||
help();
|
||||
return -1;
|
||||
}
|
||||
|
||||
TAILQ_INIT(&hole_files);
|
||||
scan_dir(fill_dirpath);
|
||||
|
||||
hole_table_fixup();
|
||||
blob_write();
|
||||
|
||||
hole_files_destroy();
|
||||
free(fill_dirpath);
|
||||
|
||||
if (output_override) {
|
||||
free(output_path);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user