From 90af8825a41981ce2ee52e0a9ce84f624eb022e6 Mon Sep 17 00:00:00 2001
From: dzwdz
Date: Tue, 26 Jul 2022 00:38:45 +0200
Subject: move user_bootstrap to user/bootstrap for consistency's sake

---
 src/user/bootstrap/linker.ld |  37 ++++++++++
 src/user/bootstrap/main.c    |  36 ++++++++++
 src/user/bootstrap/tar.c     | 159 +++++++++++++++++++++++++++++++++++++++++++
 src/user/bootstrap/tar.h     |   5 ++
 4 files changed, 237 insertions(+)
 create mode 100644 src/user/bootstrap/linker.ld
 create mode 100644 src/user/bootstrap/main.c
 create mode 100644 src/user/bootstrap/tar.c
 create mode 100644 src/user/bootstrap/tar.h

(limited to 'src/user')

diff --git a/src/user/bootstrap/linker.ld b/src/user/bootstrap/linker.ld
new file mode 100644
index 0000000..917388c
--- /dev/null
+++ b/src/user/bootstrap/linker.ld
@@ -0,0 +1,37 @@
+ENTRY(main)
+OUTPUT_FORMAT("binary")
+
+SECTIONS
+{
+	/* Not an ELF, but we need this to link with elfreloc.c.
+	 * Not that we need to link with it, it's just easier that way */
+	_DYNAMIC = 0;
+	_image_base = 0;
+
+	. = 2M;
+	.text BLOCK(4K) : ALIGN(4K)
+	{
+		*(.text.startup)
+		*(.text)
+	}
+	.rodata BLOCK(4K) : ALIGN(4K)
+	{
+		*(.rodata)
+	}
+	.data BLOCK(4K) : ALIGN(4K)
+	{
+		*(.data)
+	}
+
+	. = ALIGN(32); /* seems to be necessary? */
+	_initrd = .; /* is just appended onto the end of the binary */
+	. += 2M;
+
+	_bss_start = .;
+	.bss BLOCK(4K) : ALIGN(4K)
+	{
+		*(COMMON)
+		*(.bss)
+	}
+	_bss_end = .;
+}
diff --git a/src/user/bootstrap/main.c b/src/user/bootstrap/main.c
new file mode 100644
index 0000000..5f36aa1
--- /dev/null
+++ b/src/user/bootstrap/main.c
@@ -0,0 +1,36 @@
+#include <shared/flags.h>
+#include <shared/mem.h>
+#include <shared/syscalls.h>
+#include <user/lib/elfload.h>
+#include <user/lib/fs/misc.h>
+
+#include "tar.h"
+
+extern char _bss_start;
+extern char _bss_end;
+extern char _initrd;
+
+__attribute__((section(".text.startup")))
+int main(void) {
+	_syscall_memflag(&_bss_start, &_bss_end - &_bss_start, MEMFLAG_PRESENT);
+
+	/* move everything provided by the kernel to /kdev */
+	MOUNT("/kdev/", fs_passthru(NULL));
+	if (!fork2_n_mount("/")) {
+		const char *l[] = {"/kdev/", NULL};
+		fs_whitelist(l);
+	}
+	if (!fork2_n_mount("/")) fs_dir_inject("/kdev/"); // TODO should be part of fs_whitelist
+
+	MOUNT("/init/", tar_driver(&_initrd));
+
+	void *init = tar_find("init.elf", 8, &_initrd, ~0) + 512;
+	if (init) {
+		_klogf("execing init.elf");
+		elf_exec(init);
+		_klogf("elf_exec failed");
+	} else {
+		_klogf("couldn't find init.elf");
+	}
+	_syscall_exit(1);
+}
diff --git a/src/user/bootstrap/tar.c b/src/user/bootstrap/tar.c
new file mode 100644
index 0000000..40da437
--- /dev/null
+++ b/src/user/bootstrap/tar.c
@@ -0,0 +1,159 @@
+#include <shared/flags.h>
+#include <shared/mem.h>
+#include <shared/syscalls.h>
+#include <stdint.h>
+
+#include "tar.h"
+
+#define BUF_SIZE 64
+
+static void *tar_open(const char *path, int len, void *base, size_t base_len);
+static void tar_read(struct fs_wait_response *res, void *base, size_t base_len);
+static int tar_size(void *sector);
+static int oct_parse(char *str, size_t len);
+
+
+static const char *root_fakemeta = ""; /* see comment in tar_open */
+
+
+void tar_driver(void *base) {
+	static char buf[BUF_SIZE];
+	struct fs_wait_response res;
+	void *ptr;
+	while (!_syscall_fs_wait(buf, BUF_SIZE, &res)) {
+		switch (res.op) {
+			case VFSOP_OPEN:
+				ptr = tar_open(buf, res.len, base, ~0);
+				_syscall_fs_respond(ptr, ptr ? 0 : -1, 0);
+				break;
+
+			case VFSOP_READ:
+				tar_read(&res, base, ~0);
+				break;
+
+			default:
+				_syscall_fs_respond(NULL, -1, 0); // unsupported
+				break;
+		}
+	}
+	_syscall_exit(0);
+}
+
+static void *tar_open(const char *path, int len, void *base, size_t base_len) {
+	if (len <= 0) return NULL;
+	path += 1; // skip the leading slash
+	len  -= 1;
+
+	/* TAR archives don't (seem to) contain an entry for the root dir, so i'm
+	 * returning a fake one. this isn't a full entry because i'm currently too
+	 * lazy to create a full one - thus, it has to be special cased in tar_read */
+	if (len == 0)
+		return (void*)root_fakemeta;
+
+	return tar_find(path, len, base, base_len);
+}
+
+static void tar_read(struct fs_wait_response *res, void *base, size_t base_len) {
+	void *meta =  (void*)res->id;
+	char  type = *(char*)(meta + 156);
+	size_t meta_len;
+	int size;
+
+	static char buf[BUF_SIZE]; // TODO reuse a single buffer
+	size_t buf_pos = 0;
+
+	if (meta == root_fakemeta) type = '5'; /* see comment in tar_open() */
+
+	switch (type) {
+		case '\0':
+		case '0': /* normal files */
+			size = tar_size(meta);
+			if (res->offset < 0 || res->offset > size) {
+				// TODO support negative offsets
+				_syscall_fs_respond(NULL, -1, 0);
+			} else {
+				_syscall_fs_respond(meta + 512 + res->offset, size - res->offset, 0);
+			}
+			break;
+
+		case '5': /* directory */
+			meta_len = strlen(meta);
+			size_t to_skip = res->offset;
+
+			/* find files in dir */
+			for (size_t off = 0; off < base_len;) {
+				if (0 != memcmp(base + off + 257, "ustar", 5))
+					break; // not a metadata sector
+				// TODO more meaningful variable names and clean code up
+
+				/* check if prefix matches */
+				if (0 == memcmp(base + off, meta, meta_len) &&
+						*(char*)(base + off + meta_len) != '\0') {
+					char *suffix = base + off + meta_len;
+					size_t suffix_len = strlen(suffix);
+
+					/* check if the path contains any non-trailing slashes */
+					char *next = suffix;
+					while (*next && *next != '/') next++;
+					if (*next == '/') next++;
+					if (*next == '\0') {
+						if (to_skip > suffix_len) {
+							to_skip -= suffix_len;
+						} else {
+							suffix += to_skip;
+							suffix_len -= to_skip;
+							to_skip = 0;
+
+							/* it doesn't - so let's add it to the result */
+							memcpy(buf + buf_pos, suffix, suffix_len);
+							buf[buf_pos + suffix_len] = '\0';
+							buf_pos += suffix_len + 1;
+							// TODO no buffer overrun check
+						}
+					}
+				}
+
+				size = tar_size(base + off);
+				off += 512;                 // skip this metadata sector
+				off += (size + 511) & ~511; // skip the data sectors
+			}
+
+			_syscall_fs_respond(buf, buf_pos, 0);
+			break;
+
+		default:
+			_syscall_fs_respond(NULL, -1, 0);
+			break;
+	}
+}
+
+static int tar_size(void *sector) {
+	return oct_parse(sector + 124, 11);
+}
+
+void *tar_find(const char *path, size_t path_len, void *base, size_t base_len) {
+	int size;
+	if (path_len > 100) return NULL; // illegal path
+
+	for (size_t off = 0; off < base_len;) {
+		if (0 != memcmp(base + off + 257, "ustar", 5))
+			break; // not a metadata sector
+		if (0 == memcmp(base + off, path, path_len) &&
+				*(char*)(base + off + path_len) == '\0')
+			return base + off; // file found, quit
+
+		size = tar_size(base + off);
+		off += 512;                 // skip this metadata sector
+		off += (size + 511) & ~511; // skip the data sectors
+	}
+	return NULL;
+}
+
+static int oct_parse(char *str, size_t len) {
+	int res = 0;
+	for (size_t i = 0; i < len; i++) {
+		res *= 8;
+		res += str[i] - '0'; // no format checking
+	}
+	return res;
+}
diff --git a/src/user/bootstrap/tar.h b/src/user/bootstrap/tar.h
new file mode 100644
index 0000000..43aa9ed
--- /dev/null
+++ b/src/user/bootstrap/tar.h
@@ -0,0 +1,5 @@
+#pragma once
+#include <shared/types.h>
+
+_Noreturn void tar_driver(void *base);
+void *tar_find(const char *path, size_t path_len, void *base, size_t base_len);
-- 
cgit v1.2.3