summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIan Moffett <ian@osmora.org>2025-09-13 14:53:43 -0400
committerIan Moffett <ian@osmora.org>2025-09-13 14:53:43 -0400
commite698061ebaf0cf1a10c11c51a6c6cd46a331959c (patch)
tree19faff0de74be4661e55478ee7d31a8427c067c6
initial commit
Signed-off-by: Ian Moffett <ian@osmora.org>
-rw-r--r--.gitignore7
-rw-r--r--README.md1
-rw-r--r--src/Makefile38
-rw-r--r--src/arch/i386/conf/e0.ld46
-rw-r--r--src/data/boot/limine.conf11
-rwxr-xr-xsrc/tools/bootstrap61
-rw-r--r--src/tools/build-toolchain.sh33
-rwxr-xr-xsrc/tools/checknl.pl87
-rw-r--r--src/tools/omar/Makefile12
-rw-r--r--src/tools/omar/README9
-rw-r--r--src/tools/omar/omar.c470
-rw-r--r--src/tools/predock.sh77
12 files changed, 852 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..0d0c81e
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,7 @@
+/src/cc
+/src/shim/
+/src/tools/omar/bin/omar
+/src/root/
+*.o
+*.d
+*.iso
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..b7b443f
--- /dev/null
+++ b/README.md
@@ -0,0 +1 @@
+# LUNOS
diff --git a/src/Makefile b/src/Makefile
new file mode 100644
index 0000000..ea6c7aa
--- /dev/null
+++ b/src/Makefile
@@ -0,0 +1,38 @@
+TARGET = x86_64
+SHIMDIR = shim
+SHIMBIN = BOOTX64.EFI
+SYSROOT = root
+OMAR = tools/omar/bin/omar
+ISO = lunos.iso
+
+# QEMU emulator flags
+QEMU_FLAGS = --enable-kvm -serial stdio -cdrom $(ISO) \
+ -M q35 -cpu host -smp 4 -m 2G
+
+.PHONY: all
+all: root image
+
+root:
+ mkdir -p root/
+ mkdir -p root/boot/
+
+.PHONY: sysroot
+image:
+ $(OMAR) -i $(SYSROOT) -o $(SYSROOT)/boot/initrd.omar
+ cp $(SHIMDIR)/limine/limine-bios-cd.bin $(SYSROOT)/
+ cp $(SHIMDIR)/limine/limine-uefi-cd.bin $(SYSROOT)/
+ cp $(SHIMDIR)/limine/limine-bios.sys $(SYSROOT)/
+ cp data/boot/limine.conf $(SYSROOT)/boot/limine.conf
+ xorriso -as mkisofs -b limine-bios-cd.bin -no-emul-boot -boot-load-size 4\
+ -boot-info-table --efi-boot limine-uefi-cd.bin -efi-boot-part \
+ --efi-boot-image --protective-msdos-label $(SYSROOT) -o $(ISO) 1>/dev/null
+ $(SHIMDIR)/limine/limine bios-install $(ISO) 1>/dev/null
+
+
+.PHONY: qemu-amd64
+run:
+ qemu-system-$(TARGET) $(QEMU_FLAGS)
+
+.PHONY: clean
+clean:
+ rm -f lunos.iso
diff --git a/src/arch/i386/conf/e0.ld b/src/arch/i386/conf/e0.ld
new file mode 100644
index 0000000..5b7c7fc
--- /dev/null
+++ b/src/arch/i386/conf/e0.ld
@@ -0,0 +1,46 @@
+OUTPUT_FORMAT(elf64-x86-64)
+OUTPUT_ARCH(i386:x86-64)
+ENTRY(__bsp_entry)
+
+PHDRS
+{
+ text PT_LOAD FLAGS((1 << 0) | (1 << 2)) ;
+ rodata PT_LOAD FLAGS((1 << 2)) ;
+ data PT_LOAD FLAGS((1 << 1) | (1 << 2)) ;
+}
+
+SECTIONS
+{
+ . = 0xFFFFFFFF80000000;
+
+ .text : {
+ *(.text .text.*)
+ } :text
+
+ . += CONSTANT(MAXPAGESIZE);
+
+ .rodata : {
+ *(.rodata .rodata.*)
+ } :rodata
+
+ . += CONSTANT(MAXPAGESIZE);
+
+ .data : {
+ *(.data)
+ } :data
+
+ .bss : {
+ *(COMMON)
+ *(.bss .bss.*)
+ } :data
+
+ . = ALIGN(64);
+ .data.cacheline_aligned : {
+ *(.data.cacheline_aligned)
+ }
+
+ /DISCARD/ : {
+ *(.eh_frame .eh_frame.*)
+ *(.note .note.*)
+ }
+}
diff --git a/src/data/boot/limine.conf b/src/data/boot/limine.conf
new file mode 100644
index 0000000..6d33d8a
--- /dev/null
+++ b/src/data/boot/limine.conf
@@ -0,0 +1,11 @@
+timeout: 15
+randomize_hhdm_base: no
+${WALLPAPER_PATH}=boot():/boot/wallpaper.jpg
+wallpaper: ${WALLPAPER_PATH}
+term_background: 40000000
+resolution: 1280x720
+
+/e0
+ protocol: multiboot2
+ kernel_path: boot():/boot/e0
+ module_path: boot():/boot/initrd.omar
diff --git a/src/tools/bootstrap b/src/tools/bootstrap
new file mode 100755
index 0000000..66664fa
--- /dev/null
+++ b/src/tools/bootstrap
@@ -0,0 +1,61 @@
+#!/bin/bash
+set -e
+
+SYSTEM_NAME="$(uname -s)"
+MAKE="make"
+GIT="git"
+GCC="gcc"
+CLANG="clang"
+
+if [ "$SYSTEM_NAME" = "OpenBSD" ]
+then
+ MAKE="$(which gmake)"
+fi
+
+# arg0: Output path.
+# arg1: Command for downloading
+try_fetch() {
+ if [[ -d $2 ]]
+ then
+ echo "try_fetch: Skipping $2: already exists"
+ else
+ ${@:1}
+ fi
+}
+
+fetch() {
+ mkdir -p cc/
+ try_fetch "git clone https://github.com/limine-bootloader/limine.git --branch=v9.3.0-binary --depth=1" "shim/limine"
+ try_fetch "git clone https://github.com/EthosZero/toolchain --depth=1" "cc/toolchain"
+}
+
+build_limine() {
+ $MAKE -C shim/limine/
+}
+
+build_omar() {
+ $MAKE -C tools/omar
+}
+
+build() {
+ build_limine
+ build_omar
+}
+
+echo "----------------------------------"
+echo
+echo " Fetching sources... "
+echo
+echo "----------------------------------"
+echo -e "\n"
+
+fetch # Fetch sources
+
+echo "----------------------------------"
+echo
+echo " Building sources... "
+echo
+echo "----------------------------------"
+echo -e "\n"
+
+build # Build sources
diff --git a/src/tools/build-toolchain.sh b/src/tools/build-toolchain.sh
new file mode 100644
index 0000000..ca7fd58
--- /dev/null
+++ b/src/tools/build-toolchain.sh
@@ -0,0 +1,33 @@
+#!/bin/bash
+
+TARGET=x86_64-pc-ethos
+MAKE=make
+
+# Don't build again if the lock exists
+if [[ -f cc/.lock ]]
+then
+ echo "cc/.lock exists, skipping toolchain build"
+ exit 1
+fi
+
+# Build binutils and patch gcc
+cd cc/toolchain/
+bash build.sh
+
+# Prep the build directory
+cd ../
+mkdir -p gcc
+cd gcc/
+
+# Configure gcc
+../toolchain/gcc-patched/configure --target=$TARGET \
+ --prefix=$(pwd) --with-sysroot=$(pwd)/../../root/ \
+ --disable-nls --enable-languages=c --disable-multilib
+
+# Build gcc
+$MAKE all-gcc
+$MAKE install-gcc
+
+# Lock the directory
+cd ../
+touch .lock
diff --git a/src/tools/checknl.pl b/src/tools/checknl.pl
new file mode 100755
index 0000000..48f8c5d
--- /dev/null
+++ b/src/tools/checknl.pl
@@ -0,0 +1,87 @@
+#!/bin/perl
+#
+# Copyright (c) 2025 Ian Marco Moffett and Ethos0 engineers
+# 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 the project 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.
+#
+
+use strict;
+use warnings;
+
+#
+# Fetch a single line from a file, returns eof when there
+# are no more lines to fetch.
+#
+# Arg0: File handle
+#
+sub get_line {
+ my $fhand = shift;
+ return <$fhand> || die "Failed to read file...";
+}
+
+my $argc = 0+@ARGV;
+
+if ($argc < 1) {
+ die "Usage: $0 <filename>";
+}
+
+# Grab the file
+my $clean = 1;
+my $lineno = 1;
+my $RS = undef;
+my $file = shift;
+open my $fhand, '<', $file || die "Failed to open $file: $^E";
+my $tmp = get_line($fhand);
+
+# Is there a newline at the start of the file?
+if ($tmp =~ /^\n/) {
+ print STDERR "Found redundant newline at start of file\n";
+ $clean = 0;
+}
+
+while (!eof($fhand)) {
+ my $cur = get_line($fhand);
+
+ if ($cur =~ /^\n$/ && $tmp =~ /^\n$/) {
+ print STDERR "Found redundant newline @ $lineno\n";
+ $clean = 0;
+ }
+
+ ++$lineno;
+ $tmp = $cur;
+}
+
+# What about at the end?
+if ($tmp =~ /^\n/) {
+ print STDERR "Found redundant newline at end of file\n";
+ $clean = 0;
+}
+
+if (!$clean) {
+ exit(1);
+}
+
+print STDOUT "File is clean\n"
diff --git a/src/tools/omar/Makefile b/src/tools/omar/Makefile
new file mode 100644
index 0000000..ee30260
--- /dev/null
+++ b/src/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/src/tools/omar/README b/src/tools/omar/README
new file mode 100644
index 0000000..3c74d78
--- /dev/null
+++ b/src/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/src/tools/omar/omar.c b/src/tools/omar/omar.c
new file mode 100644
index 0000000..a4c7ad6
--- /dev/null
+++ b/src/tools/omar/omar.c
@@ -0,0 +1,470 @@
+/*
+ * 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
+
+/* Revision */
+#define OMAR_REV 2
+
+#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
+ * @rev: OMAR revision
+ * @mode: File permissions
+ */
+struct omar_hdr {
+ char magic[4];
+ uint8_t type;
+ uint8_t namelen;
+ uint32_t len;
+ uint8_t rev;
+ uint32_t mode;
+} __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(struct omar_hdr *hdr, 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, hdr->mode);
+ *p = '/';
+ }
+ }
+
+ mkdir(buf, hdr->mode);
+}
+
+/*
+ * 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.mode = sb.st_mode;
+ }
+
+ hdr.len = (pathname == NULL) ? 0 : sb.st_size;
+ hdr.rev = OMAR_REV;
+ 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
+ *
+ * @hp: File header
+ * @data: Data to extract
+ * @len: Length of data
+ * @path: Path to output file
+ */
+static int
+extract_single(struct omar_hdr *hp, char *data, size_t len, const char *path)
+{
+ int fd;
+
+ if ((fd = open(path, O_WRONLY | O_CREAT, hp->mode)) < 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;
+ size_t len;
+ off_t off;
+ char namebuf[256];
+ char pathbuf[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;
+ }
+ if (hdr->rev != OMAR_REV) {
+ fprintf(stderr, "cannot extract rev %d archive\n", hdr->rev);
+ fprintf(stderr, "current OMAR revision: %d\n", OMAR_REV);
+ }
+
+ name = (char *)hdr + sizeof(struct omar_hdr);
+ memcpy(namebuf, name, hdr->namelen);
+ namebuf[hdr->namelen] = '\0';
+
+ /* Get the full path */
+ len = snprintf(pathbuf, sizeof(pathbuf), "%s/%s", outpath, namebuf);
+ if (len < 0) {
+ free(buf);
+ return len;
+ }
+ printf("unpacking %s\n", pathbuf);
+
+ if (hdr->type == OMAR_DIR) {
+ off = 512;
+ mkpath(hdr, pathbuf);
+ } else {
+ off = ALIGN_UP(sizeof(*hdr) + hdr->namelen + hdr->len, BLOCK_SIZE);
+ p = (char *)hdr + sizeof(struct omar_hdr);
+ p += hdr->namelen;
+ extract_single(hdr, p, hdr->len, pathbuf);
+ }
+
+ 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;
+}
diff --git a/src/tools/predock.sh b/src/tools/predock.sh
new file mode 100644
index 0000000..f2f74ac
--- /dev/null
+++ b/src/tools/predock.sh
@@ -0,0 +1,77 @@
+#!/bin/bash
+#
+# Copyright (c) 2025 Ian Marco Moffett and Ethos0 engineers
+# 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 the project 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.
+
+#
+# Description: Checks if 'bay-0' is OK to merge with 'main'
+# Author: Ian Marco Moffett
+#
+set -e
+
+# Directories to check
+SYSTEM=$(find sys/ -type f -name "[!.]*")
+LIB=$(find lib/ -type f -name "[!.]*")
+TOOLS=$(find tools/ -type f -name "[!.]*")
+
+fail() {
+ echo "!! TEST FAILURE !!"
+ exit 1
+}
+
+#
+# $1: List of files
+#
+check_folder() {
+ for file in $1
+ do
+ echo "predock: check $file..."
+ if [[ ! -f $file ]]
+ then
+ echo "skip $file"
+ continue
+ fi
+ tools/checknl.pl $file || fail
+ echo "OK"
+ done
+}
+
+echo "predock: checking bay..."
+echo "predock: nuking artifacts..."
+make distclean || fail
+./build.sh || fail
+
+echo "predock: CHECK SYSTEM"
+check_folder "$SYSTEM"
+echo "predock: OK, CHECK LIB..."
+check_folder "$LIB"
+echo "predock: OK, CHECK TOOLS..."
+check_folder "$TOOLS"
+echo "---------------------------------------"
+echo "predock: ALL CHECKS PASSED"
+echo "predock: IT IS SAFE TO DOCK BAY WITH 'main'"
+git status -s