kernel: initrd unpacking

This commit is contained in:
Matheus
2023-05-01 00:47:01 -03:00
parent d90f8d0950
commit ade8aa4e2b
3 changed files with 190 additions and 0 deletions
+2
View File
@@ -16,6 +16,7 @@
#include <kernel/scheduler.h>
#include <kernel/vfs.h>
#include <kernel/tmpfs.h>
#include <kernel/initrd.h>
static cpu_t bsp_cpu;
@@ -43,5 +44,6 @@ void kernel_entry() {
tmpfs_init();
printf("mounting tmpfs on /\n");
__assert(vfs_mount(NULL, vfsroot, "/", "tmpfs", NULL) == 0);
initrd_unpack();
__assert(!"kernel entry end");
}
+182
View File
@@ -0,0 +1,182 @@
#include <kernel/initrd.h>
#include <limine.h>
#include <string.h>
#include <logging.h>
#include <util.h>
#include <kernel/vmm.h>
#include <kernel/pmm.h>
#include <kernel/abi.h>
#include <time.h>
#include <kernel/vfs.h>
#define TAR_BLOCKSIZE 512
#define TAR_FILE 0
#define TAR_HARDLINK 1
#define TAR_SYMLINK 2
#define TAR_CHARDEV 3
#define TAR_BLOCK 4
#define TAR_DIR 5
#define TAR_FIFO 6
typedef struct {
char name[100];
char mode[8];
char uid[8];
char gid[8];
char size[12];
char modtime[12];
char checksum[8];
char type[1];
char link[100];
char indicator[6];
char version[2];
char uname[32];
char gname[32];
char devmajor[8];
char devminor[8];
char prefix[155];
} tarheader_t;
typedef struct {
char name[256];
mode_t mode;
gid_t gid;
uid_t uid;
size_t size;
timespec_t modtime;
short checksum;
short type;
char link[101];
char indicator[6];
char version[2];
short devminor;
short devmajor;
} tarentry_t;
static size_t convert(char *buff, size_t len) {
size_t result = 0;
while (len--) {
result *= 8;
result += *buff - '0';
buff++;
}
return result;
}
static void buildentry(tarentry_t *entry, void *addr) {
tarheader_t *header = addr;
char *namecpyptr = entry->name;
// name has a prefix
if (header->name[99]) {
size_t len = header->prefix[154] ? 155 : strlen(header->prefix);
memcpy(namecpyptr, header->prefix, len);
namecpyptr += len;
}
size_t namelen = header->name[99] ? 99 : strlen(header->name);
memcpy(namecpyptr, header->name, namelen);
entry->name[namelen] = '\0';
entry->mode = convert(header->mode, 7);
entry->uid = convert(header->uid, 7);
entry->gid = convert(header->gid, 7);
entry->size = convert(header->size, 11);
entry->modtime.s = convert(header->modtime, 11);
entry->modtime.ns = 0;
entry->checksum = convert(header->checksum, 7);
entry->type = convert(header->type, 1);
entry->devmajor = convert(header->devmajor, 7);
entry->devminor = convert(header->devminor, 7);
if (header->link[99]) {
memcpy(entry->link, header->link, 100);
entry->link[100] = '\0';
} else {
strcpy(entry->link, header->link);
}
memcpy(entry->indicator, header->indicator, 6);
memcpy(entry->version, header->version, 2);
}
static volatile struct limine_module_request modreq = {
.id = LIMINE_MODULE_REQUEST,
.revision = 0
};
void initrd_unpack() {
__assert(modreq.response);
struct limine_file *initrd = NULL;
for (int i = 0; i < modreq.response->module_count; ++i) {
if (strcmp(modreq.response->modules[i]->path, "/initrd") == 0) {
initrd = modreq.response->modules[i];
break;
}
}
__assert(initrd);
printf("initrd at %p with size %lu (%lu pages)\n", initrd->address, initrd->size, ROUND_UP(initrd->size, PAGE_SIZE) / PAGE_SIZE);
__assert(((uintptr_t)initrd->address % PAGE_SIZE) == 0);
void *ptr = initrd->address;
tarentry_t entry;
void *cleanupptr = initrd->address;
size_t cleanupbytespassed = 0;
for (;;) {
if (cleanupbytespassed >= PAGE_SIZE) {
size_t pagec = cleanupbytespassed / PAGE_SIZE;
pmm_free(FROM_HHDM(cleanupptr), pagec);
cleanupptr = (void *)((uintptr_t)cleanupptr + ROUND_DOWN(cleanupbytespassed, PAGE_SIZE));
cleanupbytespassed %= PAGE_SIZE;
}
buildentry(&entry, ptr);
if (strncmp("ustar", entry.indicator, 5))
break;
vattr_t entryattr;
entryattr.gid = entry.gid;
entryattr.uid = entry.uid;
entryattr.mode = entry.mode;
void* datastart = (void *)((uintptr_t)ptr + TAR_BLOCKSIZE);
ptr = (void *)((uintptr_t)datastart + ROUND_UP(entry.size, TAR_BLOCKSIZE));
cleanupbytespassed += TAR_BLOCKSIZE;
int err = 0;
vnode_t *node;
size_t writecount;
switch (entry.type) {
case TAR_FILE:
cleanupbytespassed += ROUND_UP(entry.size, TAR_BLOCKSIZE);
err = vfs_create(vfsroot, entry.name, &entryattr, V_TYPE_REGULAR, &node);
if (err)
break;
err = vfs_write(node, datastart, entry.size, 0, &writecount, 0);
VOP_RELEASE(node);
break;
case TAR_DIR:
err = vfs_create(vfsroot, entry.name, &entryattr, V_TYPE_DIR, &node);
if (err)
break;
VOP_RELEASE(node);
break;
case TAR_SYMLINK:
err = vfs_link(NULL, entry.link, vfsroot, entry.name, V_TYPE_LINK, &entryattr);
break;
default:
__assert(!"Unsupported file type");
break;
}
if (err)
printf("initrd: failed to unpack %s: %lu\n", entry.name, err);
}
// free remaining pages to be freed
pmm_free(FROM_HHDM(cleanupptr), ROUND_UP(cleanupbytespassed, PAGE_SIZE) / PAGE_SIZE);
}
+6
View File
@@ -0,0 +1,6 @@
#ifndef _INITRD_H
#define _INITRD_H
void initrd_unpack();
#endif