From 788c45fcaba9e64d84a1a293217bb532259168c7 Mon Sep 17 00:00:00 2001 From: Ian Moffett Date: Fri, 2 May 2025 12:15:55 -0400 Subject: build: Add -no-pie to linker flags Some hosts like OpenBSD like creating position independent executables by default for security reasons... However this does not work for bare metal applications and we must ensure this always remains off. Signed-off-by: Ian Moffett --- Makefile.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Makefile.in') diff --git a/Makefile.in b/Makefile.in index 35595b1..f068df6 100644 --- a/Makefile.in +++ b/Makefile.in @@ -9,7 +9,7 @@ override ARCH = @ARCH@ override HYRA_VERSION = @PACKAGE_VERSION@ override PROMPT := printf "%s\t\t%s\n" override KERNEL_CFLAGS = @KERNEL_CFLAGS@ $(KERNEL_DEFINES) -override KERNEL_LDFLAGS = -nostdlib -znoexecstack -zmax-page-size=0x1000 -static -Tsys/arch/$(ARCH)/conf/link.ld +override KERNEL_LDFLAGS = -no-pie -nostdlib -znoexecstack -zmax-page-size=0x1000 -static -Tsys/arch/$(ARCH)/conf/link.ld override QEMU_FLAGS = @QEMU_FLAGS@ override KERNEL_DEFINES = $ -DHYRA_VERSION="\"$(HYRA_VERSION)\""\ -DHYRA_BUILDDATE="\"@HYRA_BUILDDATE@\""\ -- cgit v1.2.3 From a9545970af4c35e7135c8005a9a94e21ca19623c Mon Sep 17 00:00:00 2001 From: Ian Moffett Date: Fri, 2 May 2025 12:19:38 -0400 Subject: build: Add @PROJECT_ROOT@ build var Add project root build var as some hosts may not handle relative paths the same as others. Signed-off-by: Ian Moffett --- Makefile.in | 1 + configure.ac | 2 ++ 2 files changed, 3 insertions(+) (limited to 'Makefile.in') diff --git a/Makefile.in b/Makefile.in index f068df6..2effa66 100644 --- a/Makefile.in +++ b/Makefile.in @@ -4,6 +4,7 @@ override PROMPT := printf "%s\t\t%s\n" ############################### # CFLAGS, QEMU flags + misc ############################### +override PROJECT_ROOT = @PROJECT_ROOT@ override BOOT_FW = @BOOT_FW@ override ARCH = @ARCH@ override HYRA_VERSION = @PACKAGE_VERSION@ diff --git a/configure.ac b/configure.ac index 3729adb..79269c5 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,7 @@ AC_INIT([Hyra], [1.6], [ian@osmora.org]) TARGET="amd64" +PROJECT_ROOT=`pwd` BOOT_FW="BOOTX64.EFI" AC_ARG_ENABLE([aarch64], [AS_HELP_STRING([--enable-aarch64], [Enable AARCH64 Support])], @@ -53,6 +54,7 @@ AC_SUBST(QEMU_FLAGS, [$QEMU_FLAGS_AMD64]) AC_SUBST(QEMU, [qemu-system-x86_64]) AC_SUBST(BOOT_FW, [$BOOT_FW]) AC_SUBST(ARCH, [$TARGET]) +AC_SUBST(PROJECT_ROOT, [$PROJECT_ROOT]) AC_SUBST(TOOLCHAIN, [clang]) AC_CONFIG_FILES([Makefile]) AC_OUTPUT -- cgit v1.2.3 From c538324f4cce3183a2e068d18ddc047e0e7043a5 Mon Sep 17 00:00:00 2001 From: Ian Moffett Date: Fri, 2 May 2025 12:26:53 -0400 Subject: build: Pass project root to other build dirs Signed-off-by: Ian Moffett --- Makefile.in | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'Makefile.in') diff --git a/Makefile.in b/Makefile.in index 2effa66..9014e2d 100644 --- a/Makefile.in +++ b/Makefile.in @@ -73,20 +73,19 @@ all: base libc sbin bin base/boot/hyra-kernel ramfs iso .PHONY: sbin sbin: $(SBIN_MAKEDIRS) $(MAKE) -C $^ -I$(shell pwd)/builddeps \ - LDSCRIPT=$(shell pwd)/usr.sbin/link.ld USRDIR=$(USRDIR) - find $^ -type f -executable -exec mv {} base/usr/sbin/ \; + LDSCRIPT=$(shell pwd)/usr.sbin/link.ld USRDIR=$(USRDIR)\ + ROOT=$(PROJECT_ROOT) .PHONY: bin bin: $(BIN_MAKEDIRS) $(MAKE) -C $^ -I$(shell pwd)/builddeps \ - LDSCRIPT=$(shell pwd)/usr.bin/link.ld USRDIR=$(USRDIR) - find $^ -type f -executable -exec mv {} base/usr/bin/ \; + LDSCRIPT=$(shell pwd)/usr.bin/link.ld USRDIR=$(USRDIR) \ + ROOT=$(PROJECT_ROOT) .PHONY: libc libc: $(MAKE) -C lib/libc/ -I$(shell pwd)/builddeps \ - USRDIR=$(USRDIR) ARCH=$(ARCH) - cp lib/libc/build/libc.a base/usr/lib/ + USRDIR=$(USRDIR) ARCH=$(ARCH) ROOT=$(PROJECT_ROOT) .PHONY: base base: -- cgit v1.2.3 From 0331c642f92dce988446ea2ac6b69f82212f71ab Mon Sep 17 00:00:00 2001 From: Ian Moffett Date: Fri, 2 May 2025 15:42:21 -0400 Subject: 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 --- .gitignore | 1 + Makefile.in | 12 +- bootstrap | 5 + builddeps/limine.conf | 2 +- configure.ac | 1 + sys/fs/initramfs.c | 108 ++++++------ tools/omar/Makefile | 12 ++ tools/omar/README | 9 + tools/omar/omar.1 | 55 +++++++ tools/omar/omar.c | 447 ++++++++++++++++++++++++++++++++++++++++++++++++++ 10 files changed, 593 insertions(+), 59 deletions(-) create mode 100644 tools/omar/Makefile create mode 100644 tools/omar/README create mode 100644 tools/omar/omar.1 create mode 100644 tools/omar/omar.c (limited to 'Makefile.in') diff --git a/.gitignore b/.gitignore index 001bc03..c93d8a9 100644 --- a/.gitignore +++ b/.gitignore @@ -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/ diff --git a/bootstrap b/bootstrap index bea5ccf..511d49b 100755 --- a/bootstrap +++ b/bootstrap @@ -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 #include #include +#include #include #include #include #include -#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 = { @@ -91,21 +93,6 @@ get_module(const char *path, uint64_t *size) { return NULL; } -/* - * 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 * @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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; +} -- cgit v1.2.3