initial commit

Signed-off-by: Chloe M. <chloe@mensia.org>
This commit is contained in:
Chloe M.
2026-06-28 01:21:43 +00:00
commit a829418eb0
3 changed files with 378 additions and 0 deletions
+347
View File
@@ -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;
}