diff options
author | Ian Moffett <ian@osmora.org> | 2025-05-02 15:42:21 -0400 |
---|---|---|
committer | Ian Moffett <ian@osmora.org> | 2025-05-02 16:34:04 -0400 |
commit | 0331c642f92dce988446ea2ac6b69f82212f71ab (patch) | |
tree | 35ef26c38eebc2b9cbfbda516ed8dc8a1f8fabac | |
parent | 1b79c42492ea902af29f3157b5c38c89078dc3b8 (diff) |
kernel: initramfs: Add initial OMAR port
CPIO is very outdated and its implementations varies on different hosts,
best to use OSMORA Archive Format (OMAR) instead!
Signed-off-by: Ian Moffett <ian@osmora.org>
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | Makefile.in | 12 | ||||
-rwxr-xr-x | bootstrap | 5 | ||||
-rw-r--r-- | builddeps/limine.conf | 2 | ||||
-rw-r--r-- | configure.ac | 1 | ||||
-rw-r--r-- | sys/fs/initramfs.c | 108 | ||||
-rw-r--r-- | tools/omar/Makefile | 12 | ||||
-rw-r--r-- | tools/omar/README | 9 | ||||
-rw-r--r-- | tools/omar/omar.1 | 55 | ||||
-rw-r--r-- | tools/omar/omar.c | 447 |
10 files changed, 593 insertions, 59 deletions
@@ -12,5 +12,6 @@ /base /sys/include/machine /lib/libc/include/machine/ +/tools/omar/bin/ /share/misc/hwdoc autom4te.cache diff --git a/Makefile.in b/Makefile.in index 9014e2d..457173e 100644 --- a/Makefile.in +++ b/Makefile.in @@ -15,6 +15,11 @@ override QEMU_FLAGS = @QEMU_FLAGS@ override KERNEL_DEFINES = $ -DHYRA_VERSION="\"$(HYRA_VERSION)\""\ -DHYRA_BUILDDATE="\"@HYRA_BUILDDATE@\""\ -DHYRA_ARCH="\"@ARCH@\"" $(shell cat sys/arch/$(ARCH)/conf/GENERIC | tools/kconf/kconf) +###################### +# Initramfs config +###################### +override RAMFS_TOOL = @RAMFS_TOOL@ +override RAMFS_LOC = ramfs.omar ###################### # Toolchain @@ -106,9 +111,8 @@ run: .PHONY: ramfs ramfs: - cd base/; find . -name "*" | cpio --create --format=odc \ - --no-absolute-filenames > ../ramfs.cpio - $(PROMPT) " RAMFS " $(shell pwd)/ramfs.cpio + $(RAMFS_TOOL) -i base/ -o ramfs.omar + $(PROMPT) " RAMFS " $(shell pwd)/ramfs.omar .PHONY: clean clean: @@ -120,7 +124,7 @@ iso: mkdir -p iso_root/boot/ mkdir -p iso_root/EFI/BOOT/ cp stand/limine/$(BOOT_FW) iso_root/EFI/BOOT/ - mv ramfs.cpio iso_root/boot/ + mv $(RAMFS_LOC) iso_root/boot/ cp builddeps/limine.conf stand/limine/limine-bios.sys \ stand/limine/limine-bios-cd.bin stand/limine/limine-uefi-cd.bin iso_root/ cp base/boot/* iso_root/boot/ @@ -47,9 +47,14 @@ build_kconf() { $MAKE -C tools/kconf/ } +build_omar() { + $MAKE -C tools/omar/ +} + build() { build_limine build_kconf + build_omar } echo "----------------------------------" diff --git a/builddeps/limine.conf b/builddeps/limine.conf index f1907ea..308ad26 100644 --- a/builddeps/limine.conf +++ b/builddeps/limine.conf @@ -9,5 +9,5 @@ resolution: 1280x720 /Hyra protocol: limine kernel_path: boot():/boot/hyra-kernel - module_path: boot():/boot/ramfs.cpio + module_path: boot():/boot/ramfs.omar diff --git a/configure.ac b/configure.ac index 9ec378e..fcad555 100644 --- a/configure.ac +++ b/configure.ac @@ -56,5 +56,6 @@ AC_SUBST(BOOT_FW, [$BOOT_FW]) AC_SUBST(ARCH, [$TARGET]) AC_SUBST(PROJECT_ROOT, [$PROJECT_ROOT]) AC_SUBST(TOOLCHAIN, [clang]) +AC_SUBST(RAMFS_TOOL, [tools/omar/bin/omar]) AC_CONFIG_FILES([Makefile]) AC_OUTPUT diff --git a/sys/fs/initramfs.c b/sys/fs/initramfs.c index fd746ef..b12a64b 100644 --- a/sys/fs/initramfs.c +++ b/sys/fs/initramfs.c @@ -33,12 +33,16 @@ #include <sys/errno.h> #include <sys/limine.h> #include <sys/panic.h> +#include <sys/param.h> #include <sys/vnode.h> #include <fs/initramfs.h> #include <vm/dynalloc.h> #include <string.h> -#define CPIO_TRAILER "TRAILER!!!" +#define OMAR_EOF "RAMO" +#define OMAR_REG 0 +#define OMAR_DIR 1 +#define BLOCK_SIZE 512 /* * File or directory. @@ -51,20 +55,18 @@ struct initramfs_node { }; /* - * ODC CPIO header + * The OMAR file header, describes the basics + * of a file. + * + * @magic: Header magic ("OMAR") + * @len: Length of the file + * @namelen: Length of the filename */ -struct cpio_hdr { - char c_magic[6]; - char c_dev[6]; - char c_ino[6]; - char c_mode[6]; - char c_uid[6]; - char c_gid[6]; - char c_nlink[6]; - char c_rdev[6]; - char c_mtime[11]; - char c_namesize[6]; - char c_filesize[11]; +struct __packed omar_hdr { + char magic[4]; + uint8_t type; + uint8_t namelen; + uint32_t len; }; static volatile struct limine_module_request mod_req = { @@ -92,21 +94,6 @@ get_module(const char *path, uint64_t *size) { } /* - * Convert octal to base 10 - */ -static uint32_t -oct2dec(const char *in, size_t sz) -{ - size_t val = 0; - - for (size_t i = 0; i < sz; ++i) { - val = val * 8 + (in[i] - '0'); - } - - return val; -} - -/* * Get a file from initramfs * * @path: Path of file to get. @@ -115,41 +102,54 @@ oct2dec(const char *in, size_t sz) static int initramfs_get_file(const char *path, struct initramfs_node *res) { - const struct cpio_hdr *hdr; struct initramfs_node node; - uintptr_t addr; - size_t namesize, filesize; - mode_t mode; + const struct omar_hdr *hdr; + const char *p, *name; + char namebuf[256]; + off_t off; - addr = (uintptr_t)initramfs; + p = initramfs; for (;;) { - hdr = (void *)addr; - namesize = oct2dec(hdr->c_namesize, sizeof(hdr->c_namesize)); - filesize = oct2dec(hdr->c_filesize, sizeof(hdr->c_filesize)); - mode = oct2dec(hdr->c_mode, sizeof(hdr->c_mode)); + hdr = (struct omar_hdr *)p; + if (strncmp(hdr->magic, OMAR_EOF, sizeof(OMAR_EOF)) == 0) { + break; + } - /* Make sure the magic is correct */ - if (strncmp(hdr->c_magic, "070707", 6) != 0) { + /* Ensure the file is valid */ + if (strncmp(hdr->magic, "OMAR", 4) != 0) { + /* Bad magic */ + return -EINVAL; + } + if (hdr->namelen > sizeof(namebuf) - 1) { return -EINVAL; } - addr += sizeof(struct cpio_hdr); - node.path = (const char *)addr; + name = (char *)p + sizeof(struct omar_hdr); + memcpy(namebuf, name, hdr->namelen); + namebuf[hdr->namelen] = '\0'; - /* Is this the requested file? */ - if (strcmp(node.path, path) == 0) { - node.data = (void *)(addr + namesize); - node.size = filesize; - node.mode = mode; + /* Compute offset to next block */ + if (hdr->type == OMAR_DIR) { + off = 512; + } else { + off = ALIGN_UP(sizeof(*hdr) + hdr->namelen + hdr->len, BLOCK_SIZE); + } + + /* Skip header and name, right to the data */ + p = (char *)hdr + sizeof(struct omar_hdr); + p += hdr->namelen; + + if (strcmp(namebuf, path) == 0) { + node.mode = 0700; + node.size = hdr->len; + node.data = (void *)p; *res = node; return 0; } - /* Get next header and see if we are at the end */ - addr += (namesize + filesize); - if (strcmp(node.path, CPIO_TRAILER) == 0) { - break; - } + hdr = (struct omar_hdr *)((char *)hdr + off); + p = (char *)hdr; + memset(namebuf, 0, sizeof(namebuf)); } return -ENOENT; @@ -256,9 +256,9 @@ initramfs_init(struct fs_info *fip) struct mount *mp; int status; - initramfs = get_module("/boot/ramfs.cpio", &initramfs_size); + initramfs = get_module("/boot/ramfs.omar", &initramfs_size); if (initramfs == NULL) { - panic("failed to open initramfs cpio image\n"); + panic("failed to open initramfs OMAR image\n"); } status = vfs_alloc_vnode(&g_root_vnode, VDIR); diff --git a/tools/omar/Makefile b/tools/omar/Makefile new file mode 100644 index 0000000..ee30260 --- /dev/null +++ b/tools/omar/Makefile @@ -0,0 +1,12 @@ +CFILES = $(shell find . -name "*.c") +CFLAGS = -pedantic +CC = gcc + +.PHONY: all +all: + mkdir -p bin/ + $(CC) $(CFLAGS) $(CFILES) -o bin/omar + +.PHONY: clean +clean: + rm -rf bin/ diff --git a/tools/omar/README b/tools/omar/README new file mode 100644 index 0000000..3c74d78 --- /dev/null +++ b/tools/omar/README @@ -0,0 +1,9 @@ +------------------------------ +OMAR - OSMORA Archive Format +------------------------------ + +OMAR is a minimal, bullshit free archive format aimed to replace +the old and outdated CPIO (copy in/out) format. + +OMAR is designed for readonly in-memory filesystems (such as initramfs), +with simplicity, clarity and "getting it done" in mind. diff --git a/tools/omar/omar.1 b/tools/omar/omar.1 new file mode 100644 index 0000000..e59be31 --- /dev/null +++ b/tools/omar/omar.1 @@ -0,0 +1,55 @@ +.\" Copyright (c) 2025 Ian Marco Moffett and the Osmora Team. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions are met: +.\" +.\" 1. Redistributions of source code must retain the above copyright notice, +.\" this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of Hyra nor the names of its +.\" contributors may be used to endorse or promote products derived from +.\" this software without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +.\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +.\" LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +.\" POSSIBILITY OF SUCH DAMAGE. +.Dd Apr 29 2025 +.Dt OMAR 1 +.Os HYRA +.Sh NAME +.Nm omar - OSMORA Archive Format +.Sh SYNOPSIS +omar -i [input] -o [output] + +.Sh DESCRIPTION +Prepare files for use in an initramfs + +.Ft -i + input path directory + +.Ft -o + output path + +Upon creation of the archive image, OMAR will +produce pathnames through stdout with the following +types in square brackets ([]) + +.Ft f + Regular file + +.Ft d + Directory + +.Sh AUTHORS +.An Ian Moffett Aq Mt ian@osmora.org diff --git a/tools/omar/omar.c b/tools/omar/omar.c new file mode 100644 index 0000000..129303e --- /dev/null +++ b/tools/omar/omar.c @@ -0,0 +1,447 @@ +/* + * Copyright (c) 2025 Ian Marco Moffett and the Osmora Team. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Hyra nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/stat.h> +#include <sys/errno.h> +#include <stdio.h> +#include <fcntl.h> +#include <stdbool.h> +#include <stdlib.h> +#include <unistd.h> +#include <stddef.h> +#include <stdint.h> +#include <assert.h> +#include <dirent.h> +#include <string.h> +#include <libgen.h> + +/* OMAR magic constants */ +#define OMAR_MAGIC "OMAR" +#define OMAR_EOF "RAMO" + +/* OMAR type constants */ +#define OMAR_REG 0 +#define OMAR_DIR 1 + +/* OMAR modes */ +#define OMAR_ARCHIVE 0 +#define OMAR_EXTRACT 1 + +#define ALIGN_UP(value, align) (((value) + (align)-1) & ~((align)-1)) +#define BLOCK_SIZE 512 + +static int mode = OMAR_ARCHIVE; +static int outfd; +static const char *inpath = NULL; +static const char *outpath = NULL; + +/* + * The OMAR file header, describes the basics + * of a file. + * + * @magic: Header magic ("OMAR") + * @len: Length of the file + * @namelen: Length of the filename + */ +struct omar_hdr { + char magic[4]; + uint8_t type; + uint8_t namelen; + uint32_t len; +} __attribute__((packed)); + +static inline void +help(void) +{ + printf("--------------------------------------\n"); + printf("The OSMORA archive format\n"); + printf("Usage: omar -i [input_dir] -o [output]\n"); + printf("-h Show this help screen\n"); + printf("-x Extract an OMAR archive\n"); + printf("--------------------------------------\n"); +} + +/* + * Strip out root dir + * + * XXX: This is added code to work with Hyra + * initramfs. + */ +static const char * +strip_root(const char *path) +{ + const char *p; + + for (p = path; *p != '\0'; ++p) { + if (*p == '/') { + ++p; + return p; + } + } + + return NULL; +} + +/* + * Recursive mkdir + */ +static void +mkpath(const char *path) +{ + size_t len; + char buf[256]; + char cwd[256]; + char *p = NULL; + + len = snprintf(buf, sizeof(buf), "%s", path); + if (buf[len - 1] == '/') { + buf[len - 1] = '\0'; + } + for (p = (char *)buf + 1; *p != '\0'; ++p) { + if (*p == '/') { + *p = '\0'; + mkdir(buf, 0700); + *p = '/'; + } + } + + mkdir(buf, 0700); +} + +/* + * Push a file into the archive output + * + * @pathname: Full path name of file (NULL if EOF) + * @name: Name of file (for EOF, set to "EOF") + */ +static int +file_push(const char *pathname, const char *name) +{ + struct omar_hdr hdr; + struct stat sb; + int infd, rem, error; + int pad_len; + size_t len; + char *buf; + + hdr.type = OMAR_REG; + + /* Attempt to open the input file if not EOF */ + if (pathname != NULL) { + if ((infd = open(pathname, O_RDONLY)) < 0) { + perror("open"); + return infd; + } + + if ((error = fstat(infd, &sb)) < 0) { + return error; + } + + if (S_ISDIR(sb.st_mode)) { + hdr.type = OMAR_DIR; + } + } + + hdr.len = (pathname == NULL) ? 0 : sb.st_size; + hdr.namelen = strlen(name); + + /* + * If we are at the end of the file, use the OMAR_EOF + * magic constant instant of the usual OMAR_MAGIC. + */ + if (pathname == NULL) { + memcpy(hdr.magic, OMAR_EOF, sizeof(hdr.magic)); + } else { + memcpy(hdr.magic, OMAR_MAGIC, sizeof(hdr.magic)); + } + + write(outfd, &hdr, sizeof(hdr)); + write(outfd, name, hdr.namelen); + + /* If we are at the end of file, we are done */ + if (pathname == NULL) { + close(infd); + return 0; + } + + /* Pad directories to zero */ + if (hdr.type == OMAR_DIR) { + len = sizeof(hdr) + hdr.namelen; + rem = len & (BLOCK_SIZE - 1); + pad_len = BLOCK_SIZE - rem; + + buf = malloc(pad_len); + memset(buf, 0, pad_len); + write(outfd, buf, pad_len); + free(buf); + return 0; + } + + /* We need the file data now */ + buf = malloc(hdr.len); + if (buf == NULL) { + printf("out of memory\n"); + close(infd); + return -ENOMEM; + } + if (read(infd, buf, hdr.len) <= 0) { + perror("read"); + close(infd); + return -EIO; + } + + /* + * Write the actual file contents, if the file length is not + * a multiple of the block size, we'll need to pad out the rest + * to zero. + */ + write(outfd, buf, hdr.len); + len = sizeof(hdr) + (hdr.namelen + hdr.len); + rem = len & (BLOCK_SIZE - 1); + if (rem != 0) { + /* Compute the padding length */ + pad_len = BLOCK_SIZE - rem; + + buf = realloc(buf, pad_len); + memset(buf, 0, pad_len); + write(outfd, buf, pad_len); + } + close(infd); + free(buf); + return 0; +} + +/* + * Start creating an archive from the + * basepath of a directory. + */ +static int +archive_create(const char *base, const char *dirname) +{ + DIR *dp; + struct dirent *ent; + struct omar_hdr hdr; + const char *p = NULL, *p1; + char pathbuf[256]; + char namebuf[256]; + + dp = opendir(base); + if (dp == NULL) { + perror("opendir"); + return -ENOENT; + } + + while ((ent = readdir(dp)) != NULL) { + if (ent->d_name[0] == '.') { + continue; + } + + snprintf(pathbuf, sizeof(pathbuf), "%s/%s", base, ent->d_name); + snprintf(namebuf, sizeof(namebuf), "%s/%s", dirname, ent->d_name); + p1 = strip_root(namebuf); + + if (ent->d_type == DT_DIR) { + printf("%s [d]\n", p1); + file_push(pathbuf, p1); + archive_create(pathbuf, namebuf); + } else if (ent->d_type == DT_REG) { + printf("%s [f]\n", p1); + file_push(pathbuf, p1); + } + } + + return 0; +} + +/* + * Extract a single file + * + * @data: Data to extract + * @len: Length of data + * @path: Path to output file + */ +static int +extract_single(char *data, size_t len, const char *path) +{ + int fd; + + if ((fd = open(path, O_WRONLY | O_CREAT, 0700)) < 0) { + return fd; + } + + return write(fd, data, len) > 0 ? 0 : -1; +} + +/* + * Extract an OMAR archive. + * + * XXX: The input file [-i] will be the OMAR archive to + * be extracted, the output directory [-o] will be + * where the files get extracted. + */ +static int +archive_extract(void) +{ + char *buf, *name, *p; + struct stat sb; + struct omar_hdr *hdr; + int fd, error; + off_t off; + char namebuf[256]; + + if ((fd = open(inpath, O_RDONLY)) < 0) { + perror("open"); + return fd; + } + + if ((error = fstat(fd, &sb)) != 0) { + perror("fstat"); + close(fd); + return error; + } + + buf = malloc(sb.st_size); + if (buf == NULL) { + fprintf(stderr, "out of memory\n"); + close(fd); + return -ENOMEM; + } + + if (read(fd, buf, sb.st_size) <= 0) { + fprintf(stderr, "omar: no data read\n"); + close(fd); + return -EIO; + } + + hdr = (struct omar_hdr *)buf; + for (;;) { + if (memcmp(hdr->magic, OMAR_EOF, sizeof(OMAR_EOF)) == 0) { + printf("EOF!\n"); + return 0; + } + + /* Ensure the header is valid */ + if (memcmp(hdr->magic, "OMAR", 4) != 0) { + fprintf(stderr, "bad magic\n"); + break; + } + + name = (char *)hdr + sizeof(struct omar_hdr); + memcpy(namebuf, name, hdr->namelen); + namebuf[hdr->namelen] = '\0'; + printf("unpacking %s\n", namebuf); + + if (hdr->type == OMAR_DIR) { + off = 512; + mkpath(namebuf); + } else { + off = ALIGN_UP(sizeof(hdr) + hdr->namelen + hdr->len, BLOCK_SIZE); + p = (char *)hdr + sizeof(struct omar_hdr); + p += hdr->namelen; + extract_single(p, hdr->len, namebuf); + } + + hdr = (struct omar_hdr *)((char *)hdr + off); + memset(namebuf, 0, sizeof(namebuf)); + } + + free(buf); +} + +int +main(int argc, char **argv) +{ + int optc, retval; + int error, flags; + + if (argc < 2) { + help(); + return -1; + } + + while ((optc = getopt(argc, argv, "xhi:o:")) != -1) { + switch (optc) { + case 'x': + mode = OMAR_EXTRACT; + break; + case 'i': + inpath = optarg; + break; + case 'o': + outpath = optarg; + break; + case 'h': + help(); + return 0; + default: + help(); + return -1; + } + } + + if (inpath == NULL) { + fprintf(stderr, "omar: no input path\n"); + help(); + return -1; + } + if (outpath == NULL) { + fprintf(stderr, "omar: no output path\n"); + help(); + return -1; + } + + /* + * Do our specific job based on the mode + * OMAR is set to be in. + */ + switch (mode) { + case OMAR_ARCHIVE: + /* Begin archiving the file */ + outfd = open(outpath, O_WRONLY | O_CREAT, 0700); + if (outfd < 0) { + printf("omar: failed to open output file\n"); + return outfd; + } + + retval = archive_create(inpath, basename((char *)inpath)); + file_push(NULL, "EOF"); + break; + case OMAR_EXTRACT: + /* Begin extracting the file */ + if ((error = mkdir(outpath, 0700) != 0)) { + perror("mkdir"); + return error; + } + + retval = archive_extract(); + break; + } + close(outfd); + return retval; +} |