Files
Astral/kernel-src/fs/initrd.c
T
2026-07-01 15:00:46 -03:00

186 lines
4.6 KiB
C

#include <kernel/initrd.h>
#include <limine.h>
#include <string.h>
#include <logging.h>
#include <util.h>
#include <kernel/mm.h>
#include <kernel/page.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[257];
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->prefix[0]) {
size_t len = header->prefix[154] ? 155 : strlen(header->prefix);
memcpy(namecpyptr, header->prefix, len);
namecpyptr += len;
*namecpyptr++ = '/';
}
size_t namelen = header->name[99] ? 99 : strlen(header->name);
memcpy(namecpyptr, header->name, namelen);
namecpyptr += namelen;
*namecpyptr = '\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_ID,
.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;
mm_force_free_page(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;
VOP_UNLOCK(node);
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, NULL);
break;
case TAR_SYMLINK:
err = vfs_link(NULL, entry.link, vfsroot, entry.name, V_TYPE_LINK, &entryattr);
break;
case TAR_HARDLINK:
err = vfs_link(vfsroot, entry.link, vfsroot, entry.name, V_TYPE_REGULAR, &entryattr);
break;
default:
printf("initrd: bad type %d on entry %s\n", entry.type, entry.name);
__assert(!"Unsupported file type");
break;
}
if (err)
printf("initrd: failed to unpack %s: %lu\n", entry.name, err);
}
// free remaining pages to be freed
mm_force_free_page(FROM_HHDM(cleanupptr), ROUND_UP(cleanupbytespassed, PAGE_SIZE) / PAGE_SIZE);
}