diff --git a/kernel-src/arch/x86-64/main.c b/kernel-src/arch/x86-64/main.c index ad087d5c..86d3fbac 100644 --- a/kernel-src/arch/x86-64/main.c +++ b/kernel-src/arch/x86-64/main.c @@ -16,6 +16,7 @@ #include #include #include +#include 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"); } diff --git a/kernel-src/fs/initrd.c b/kernel-src/fs/initrd.c new file mode 100644 index 00000000..e5044e9f --- /dev/null +++ b/kernel-src/fs/initrd.c @@ -0,0 +1,182 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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); +} diff --git a/kernel-src/include/kernel/initrd.h b/kernel-src/include/kernel/initrd.h new file mode 100644 index 00000000..5d3b479c --- /dev/null +++ b/kernel-src/include/kernel/initrd.h @@ -0,0 +1,6 @@ +#ifndef _INITRD_H +#define _INITRD_H + +void initrd_unpack(); + +#endif