]> Projects (at) Tadryanom (dot) Me - AdrOS.git/commitdiff
initrd: switch to TAR USTAR with directory support
authorTulio A M Mendes <[email protected]>
Sat, 7 Feb 2026 18:45:01 +0000 (15:45 -0300)
committerTulio A M Mendes <[email protected]>
Sat, 7 Feb 2026 18:45:01 +0000 (15:45 -0300)
Makefile
include/initrd.h
src/arch/x86/arch_platform.c
src/drivers/initrd.c
tools/mkinitrd.c
user/init.c

index 59d22cefde96e3be8ccc880fb3f52050b51912f9..22be95f71e7768fcb1f738a185eb0f24c914b6a7 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -121,7 +121,7 @@ $(USER_ELF): user/init.c user/linker.ld
        @i686-elf-gcc -m32 -ffreestanding -fno-pie -no-pie -nostdlib -Wl,-T,user/linker.ld -o $(USER_ELF) user/init.c
 
 $(INITRD_IMG): $(MKINITRD) $(USER_ELF)
-       @./$(MKINITRD) $(INITRD_IMG) $(USER_ELF)
+       @./$(MKINITRD) $(INITRD_IMG) $(USER_ELF):bin/init.elf
 
 run: iso
        @rm -f serial.log qemu.log
index 1e8c1e21ef602cb9a4c4e458fa265831b83357e1..8dbb301d48c3f8534bcb38dd0082d16d1fa7f600 100644 (file)
@@ -4,13 +4,6 @@
 #include "fs.h"
 #include <stdint.h>
 
-typedef struct {
-    uint8_t magic; // Magic number 0xBF
-    char name[64];
-    uint32_t offset; // Offset relative to start of initrd
-    uint32_t length;
-} initrd_file_header_t;
-
 // Initialize InitRD and return the root node (directory)
 fs_node_t* initrd_init(uint32_t location);
 
index 5338dafed3f0f88f235929bd9e898994b437cc5a..757356e403f81082d52c8fa9958d219534c39918 100644 (file)
@@ -46,11 +46,11 @@ int arch_platform_start_userspace(const struct boot_info* bi) {
 
     uintptr_t entry = 0;
     uintptr_t user_sp = 0;
-    if (elf32_load_user_from_initrd("init.elf", &entry, &user_sp) != 0) {
+    if (elf32_load_user_from_initrd("/bin/init.elf", &entry, &user_sp) != 0) {
         return -1;
     }
 
-    uart_print("[ELF] starting init.elf\n");
+    uart_print("[ELF] starting /bin/init.elf\n");
 
     uart_print("[ELF] user_range_ok(entry)=");
     uart_put_char(user_range_ok((const void*)entry, 1) ? '1' : '0');
index aa545bd41cc00014e5068a223f493599b11b49c4..e339e2add1e0f5b2eba668161e944b1838c68aa6 100644 (file)
 #include "heap.h"
 #include "uart_console.h"
 
-initrd_file_header_t* file_headers;
-fs_node_t* initrd_root;
-fs_node_t* root_nodes; // Array of file nodes
-int n_root_nodes;
+#define TAR_BLOCK 512
 
-uint32_t initrd_read_impl(fs_node_t* node, uint32_t offset, uint32_t size, uint8_t* buffer);
+typedef struct {
+    char name[100];
+    char mode[8];
+    char uid[8];
+    char gid[8];
+    char size[12];
+    char mtime[12];
+    char chksum[8];
+    char typeflag;
+    char linkname[100];
+    char magic[6];
+    char version[2];
+    char uname[32];
+    char gname[32];
+    char devmajor[8];
+    char devminor[8];
+    char prefix[155];
+    char pad[12];
+} __attribute__((packed)) tar_header_t;
 
-uint32_t initrd_read(fs_node_t* node, uint32_t offset, uint32_t size, uint8_t* buffer) {
-    return initrd_read_impl(node, offset, size, buffer);
-}
+typedef struct {
+    char name[128];
+    uint32_t flags;
+    uint32_t data_offset;
+    uint32_t length;
+    int parent;
+    int first_child;
+    int next_sibling;
+} initrd_entry_t;
 
 static uint32_t initrd_location_base = 0;
 
-uint32_t initrd_read_impl(fs_node_t* node, uint32_t offset, uint32_t size, uint8_t* buffer) {
-    initrd_file_header_t header = file_headers[node->inode];
-    
-    if (offset > header.length) return 0;
-    if (offset + size > header.length)
-        size = header.length - offset;
-        
-    // Address = Base + Header_Offset + Read_Offset
-    uint8_t* src = (uint8_t*)(initrd_location_base + header.offset + offset);
-    
+static initrd_entry_t* entries = NULL;
+static fs_node_t* nodes = NULL;
+static int entry_count = 0;
+static int entry_cap = 0;
+
+static uint32_t tar_parse_octal(const char* s, size_t n) {
+    uint32_t v = 0;
+    for (size_t i = 0; i < n; i++) {
+        if (s[i] == 0) break;
+        if (s[i] < '0' || s[i] > '7') continue;
+        v = (v << 3) + (uint32_t)(s[i] - '0');
+    }
+    return v;
+}
+
+static int tar_is_zero_block(const uint8_t* p) {
+    for (int i = 0; i < TAR_BLOCK; i++) {
+        if (p[i] != 0) return 0;
+    }
+    return 1;
+}
+
+static void str_copy_n(char* dst, size_t dst_sz, const char* src, size_t src_n) {
+    if (dst_sz == 0) return;
+    size_t i = 0;
+    for (; i + 1 < dst_sz && i < src_n; i++) {
+        char c = src[i];
+        if (c == 0) break;
+        dst[i] = c;
+    }
+    dst[i] = 0;
+}
+
+static int entry_alloc(void) {
+    if (entry_count == entry_cap) {
+        int new_cap = (entry_cap == 0) ? 32 : entry_cap * 2;
+        initrd_entry_t* new_entries = (initrd_entry_t*)kmalloc(sizeof(initrd_entry_t) * (size_t)new_cap);
+        fs_node_t* new_nodes = (fs_node_t*)kmalloc(sizeof(fs_node_t) * (size_t)new_cap);
+        if (!new_entries || !new_nodes) return -1;
+
+        if (entries) {
+            memcpy(new_entries, entries, sizeof(initrd_entry_t) * (size_t)entry_count);
+            kfree(entries);
+        }
+        if (nodes) {
+            memcpy(new_nodes, nodes, sizeof(fs_node_t) * (size_t)entry_count);
+            kfree(nodes);
+        }
+
+        entries = new_entries;
+        nodes = new_nodes;
+        entry_cap = new_cap;
+    }
+
+    int idx = entry_count++;
+    memset(&entries[idx], 0, sizeof(entries[idx]));
+    memset(&nodes[idx], 0, sizeof(nodes[idx]));
+    entries[idx].parent = -1;
+    entries[idx].first_child = -1;
+    entries[idx].next_sibling = -1;
+    return idx;
+}
+
+static int entry_find_child(int parent, const char* name) {
+    int c = entries[parent].first_child;
+    while (c != -1) {
+        if (strcmp(entries[c].name, name) == 0) return c;
+        c = entries[c].next_sibling;
+    }
+    return -1;
+}
+
+static int entry_add_child(int parent, const char* name, uint32_t flags) {
+    int idx = entry_alloc();
+    if (idx < 0) return -1;
+
+    strcpy(entries[idx].name, name);
+    entries[idx].flags = flags;
+    entries[idx].parent = parent;
+
+    entries[idx].next_sibling = entries[parent].first_child;
+    entries[parent].first_child = idx;
+
+    return idx;
+}
+
+static int ensure_dir(int parent, const char* name) {
+    int child = entry_find_child(parent, name);
+    if (child != -1) return child;
+    return entry_add_child(parent, name, FS_DIRECTORY);
+}
+
+static int ensure_path_dirs(int root_idx, const char* path, char* leaf_out, size_t leaf_out_sz) {
+    int cur = root_idx;
+    const char* p = path;
+
+    while (*p == '/') p++;
+
+    char part[128];
+    while (*p != 0) {
+        size_t i = 0;
+        while (*p != 0 && *p != '/') {
+            if (i + 1 < sizeof(part)) part[i++] = *p;
+            p++;
+        }
+        part[i] = 0;
+        while (*p == '/') p++;
+
+        if (part[0] == 0) continue;
+
+        if (*p == 0) {
+            str_copy_n(leaf_out, leaf_out_sz, part, strlen(part));
+            return cur;
+        }
+
+        cur = ensure_dir(cur, part);
+        if (cur < 0) return -1;
+    }
+
+    return -1;
+}
+
+static uint32_t initrd_read_impl(fs_node_t* node, uint32_t offset, uint32_t size, uint8_t* buffer) {
+    if (!node) return 0;
+    uint32_t idx = node->inode;
+    if ((int)idx < 0 || (int)idx >= entry_count) return 0;
+
+    initrd_entry_t* e = &entries[idx];
+    if ((e->flags & FS_FILE) == 0) return 0;
+
+    if (offset > e->length) return 0;
+    if (offset + size > e->length) size = e->length - offset;
+
+    const uint8_t* src = (const uint8_t*)(initrd_location_base + e->data_offset + offset);
     memcpy(buffer, src, size);
     return size;
 }
 
-struct fs_node* initrd_finddir(struct fs_node* node, char* name) {
-    (void)node;
-    for (int i = 0; i < n_root_nodes; i++) {
-        if (strcmp(name, root_nodes[i].name) == 0)
-            return &root_nodes[i];
+static struct fs_node* initrd_finddir(struct fs_node* node, char* name) {
+    if (!node || !name) return 0;
+    int parent = (int)node->inode;
+    if (parent < 0 || parent >= entry_count) return 0;
+
+    int c = entries[parent].first_child;
+    while (c != -1) {
+        if (strcmp(entries[c].name, name) == 0) {
+            return &nodes[c];
+        }
+        c = entries[c].next_sibling;
     }
     return 0;
 }
 
+static void initrd_finalize_nodes(void) {
+    for (int i = 0; i < entry_count; i++) {
+        fs_node_t* n = &nodes[i];
+        initrd_entry_t* e = &entries[i];
+
+        strcpy(n->name, e->name);
+        n->inode = (uint32_t)i;
+        n->length = e->length;
+        n->flags = e->flags;
+
+        n->read = (e->flags & FS_FILE) ? &initrd_read_impl : 0;
+        n->write = 0;
+        n->open = 0;
+        n->close = 0;
+        n->finddir = (e->flags & FS_DIRECTORY) ? &initrd_finddir : 0;
+    }
+}
+
 fs_node_t* initrd_init(uint32_t location) {
     initrd_location_base = location;
-    
-    // Read number of files (first 4 bytes)
-    uint32_t* n_files_ptr = (uint32_t*)location;
-    n_root_nodes = *n_files_ptr;
-    
+
+    // Initialize root
+    entry_count = 0;
+
+    int root = entry_alloc();
+    if (root < 0) return 0;
+    strcpy(entries[root].name, "");
+    entries[root].flags = FS_DIRECTORY;
+    entries[root].data_offset = 0;
+    entries[root].length = 0;
+    entries[root].parent = -1;
+
+    const uint8_t* p = (const uint8_t*)(uintptr_t)location;
+    int files = 0;
+
+    while (1) {
+        if (tar_is_zero_block(p)) break;
+        const tar_header_t* h = (const tar_header_t*)p;
+
+        char name[256];
+        name[0] = 0;
+        if (h->prefix[0]) {
+            str_copy_n(name, sizeof(name), h->prefix, sizeof(h->prefix));
+            size_t cur = strlen(name);
+            if (cur + 1 < sizeof(name)) {
+                name[cur] = '/';
+                name[cur + 1] = 0;
+            }
+            size_t rem = sizeof(name) - strlen(name) - 1;
+            str_copy_n(name + strlen(name), rem + 1, h->name, sizeof(h->name));
+        } else {
+            str_copy_n(name, sizeof(name), h->name, sizeof(h->name));
+        }
+
+        uint32_t size = tar_parse_octal(h->size, sizeof(h->size));
+        char tf = h->typeflag;
+        if (tf == 0) tf = '0';
+
+        // Normalize: strip leading './'
+        if (name[0] == '.' && name[1] == '/') {
+            size_t l = strlen(name);
+            for (size_t i = 0; i + 2 <= l; i++) {
+                name[i] = name[i + 2];
+            }
+        }
+
+        // Directories in tar often end with '/'
+        size_t nlen = strlen(name);
+        if (nlen && name[nlen - 1] == '/') {
+            name[nlen - 1] = 0;
+            tf = '5';
+        }
+
+        if (name[0] != 0) {
+            char leaf[128];
+            int parent = ensure_path_dirs(root, name, leaf, sizeof(leaf));
+            if (parent >= 0) {
+                if (tf == '5') {
+                    (void)ensure_dir(parent, leaf);
+                } else {
+                    int existing = entry_find_child(parent, leaf);
+                    int idx = existing;
+                    if (idx == -1) {
+                        idx = entry_add_child(parent, leaf, FS_FILE);
+                    }
+                    if (idx >= 0) {
+                        entries[idx].flags = FS_FILE;
+                        entries[idx].data_offset = (uint32_t)((uintptr_t)(p + TAR_BLOCK) - (uintptr_t)location);
+                        entries[idx].length = size;
+                        files++;
+                    }
+                }
+            }
+        }
+
+        uint32_t adv = TAR_BLOCK + ((size + (TAR_BLOCK - 1)) & ~(TAR_BLOCK - 1));
+        p += adv;
+    }
+
+    initrd_finalize_nodes();
+
     uart_print("[INITRD] Found ");
-    char buf[16]; itoa(n_root_nodes, buf, 10);
+    char buf[16]; itoa(files, buf, 10);
     uart_print(buf);
     uart_print(" files.\n");
-    
-    // Headers start right after n_files
-    file_headers = (initrd_file_header_t*)(location + sizeof(uint32_t));
-    
-    // Allocate nodes for files
-    root_nodes = (fs_node_t*)kmalloc(sizeof(fs_node_t) * n_root_nodes);
-    
-    for (int i = 0; i < n_root_nodes; i++) {
-        strcpy(root_nodes[i].name, file_headers[i].name);
-        root_nodes[i].inode = i;
-        root_nodes[i].length = file_headers[i].length;
-        root_nodes[i].flags = FS_FILE;
-        root_nodes[i].read = &initrd_read_impl;
-        root_nodes[i].write = 0;
-        root_nodes[i].open = 0;
-        root_nodes[i].close = 0;
-        root_nodes[i].finddir = 0;
-    }
-    
-    // Create Root Directory Node
-    initrd_root = (fs_node_t*)kmalloc(sizeof(fs_node_t));
-    strcpy(initrd_root->name, "initrd");
-    initrd_root->flags = FS_DIRECTORY;
-    initrd_root->finddir = &initrd_finddir;
-    
-    return initrd_root;
+
+    return &nodes[root];
 }
index af062e589a3d7bca70a7e3469e82651ed3d77832..108fdd2f73fc281903c0a28e1c0d9278deb65ded 100644 (file)
 #include <stdio.h>
 #include <stdlib.h>
-#include <string.h>
 #include <stdint.h>
+#include <string.h>
+
+#define TAR_BLOCK 512
+
+typedef struct {
+    char name[100];
+    char mode[8];
+    char uid[8];
+    char gid[8];
+    char size[12];
+    char mtime[12];
+    char chksum[8];
+    char typeflag;
+    char linkname[100];
+    char magic[6];
+    char version[2];
+    char uname[32];
+    char gname[32];
+    char devmajor[8];
+    char devminor[8];
+    char prefix[155];
+    char pad[12];
+} __attribute__((packed)) tar_header_t;
+
+static void tar_write_octal(char* out, size_t out_sz, uint32_t val) {
+    // Write N-1 digits + NUL (or space) padding
+    // Common tar field style: leading zeros, terminated by NUL.
+    if (out_sz == 0) return;
+    memset(out, '0', out_sz);
+    out[out_sz - 1] = '\0';
+
+    size_t i = out_sz - 2;
+    uint32_t v = val;
+    while (1) {
+        out[i] = (char)('0' + (v & 7));
+        v >>= 3;
+        if (v == 0 || i == 0) break;
+        i--;
+    }
+}
+
+static uint32_t tar_checksum(const tar_header_t* h) {
+    const uint8_t* p = (const uint8_t*)h;
+    uint32_t sum = 0;
+    for (size_t i = 0; i < sizeof(*h); i++) {
+        sum += p[i];
+    }
+    return sum;
+}
+
+static void tar_write_header(FILE* out, const char* name, uint32_t size, char typeflag) {
+    tar_header_t h;
+    memset(&h, 0, sizeof(h));
 
-struct initrd_header {
-    uint8_t magic; // Unused for now
-    char name[64];
-    uint32_t offset;
-    uint32_t length;
-};
+    // Minimal USTAR header; we keep paths in the name field.
+    strncpy(h.name, name, sizeof(h.name) - 1);
+    tar_write_octal(h.mode, sizeof(h.mode), 0644);
+    tar_write_octal(h.uid, sizeof(h.uid), 0);
+    tar_write_octal(h.gid, sizeof(h.gid), 0);
+    tar_write_octal(h.size, sizeof(h.size), size);
+    tar_write_octal(h.mtime, sizeof(h.mtime), 0);
 
-int main(int argc, char **argv) {
+    memset(h.chksum, ' ', sizeof(h.chksum));
+    h.typeflag = typeflag;
+    memcpy(h.magic, "ustar", 5);
+    memcpy(h.version, "00", 2);
+
+    uint32_t sum = tar_checksum(&h);
+    // chksum is 6 digits, NUL, space
+    tar_write_octal(h.chksum, 7, sum);
+    h.chksum[6] = '\0';
+    h.chksum[7] = ' ';
+
+    fwrite(&h, 1, sizeof(h), out);
+}
+
+static void write_zeros(FILE* out, size_t n) {
+    static uint8_t z[TAR_BLOCK];
+    memset(z, 0, sizeof(z));
+    while (n) {
+        size_t chunk = n;
+        if (chunk > sizeof(z)) chunk = sizeof(z);
+        fwrite(z, 1, chunk, out);
+        n -= chunk;
+    }
+}
+
+static int split_src_dest(const char* arg, char* src_out, size_t src_sz, char* dest_out, size_t dest_sz) {
+    const char* colon = strchr(arg, ':');
+    if (!colon) return 0;
+
+    size_t src_len = (size_t)(colon - arg);
+    size_t dest_len = strlen(colon + 1);
+    if (src_len == 0 || dest_len == 0) return 0;
+    if (src_len >= src_sz || dest_len >= dest_sz) return 0;
+
+    memcpy(src_out, arg, src_len);
+    src_out[src_len] = 0;
+    memcpy(dest_out, colon + 1, dest_len);
+    dest_out[dest_len] = 0;
+    return 1;
+}
+
+int main(int argc, char* argv[]) {
     if (argc < 3) {
-        printf("Usage: %s <output> <file1> [file2] ...\n", argv[0]);
+        printf("Usage: %s output.img file1[:dest] [file2[:dest] ...]\n", argv[0]);
         return 1;
     }
-    
-    int nheader = argc - 2;
-    struct initrd_header headers[nheader];
-    
-    // Calculate offsets
-    // Data starts after: [4 bytes count] + [headers * n]
-    uint32_t data_offset = sizeof(uint32_t) + (sizeof(struct initrd_header) * nheader);
-    
-    printf("Creating InitRD with %d files...\n", nheader);
-    
-    // Prepare headers
-    for(int i = 0; i < nheader; i++) {
-        printf("Adding: %s\n", argv[i+2]);
-
-        const char* in = argv[i+2];
-        const char* base = strrchr(in, '/');
-        base = base ? (base + 1) : in;
-
-        strcpy(headers[i].name, base); // Warning: Buffer overflow unsafe, good enough for tool
-        headers[i].offset = data_offset;
-        headers[i].magic = 0xBF;
-        
-        FILE *stream = fopen(argv[i+2], "rb");
-        if(!stream) {
-            printf("Error opening file: %s\n", argv[i+2]);
-            return 1;
-        }
-        fseek(stream, 0, SEEK_END);
-        headers[i].length = ftell(stream);
-        data_offset += headers[i].length;
-        fclose(stream);
-    }
-    
-    FILE *wstream = fopen(argv[1], "wb");
-    if(!wstream) {
-        printf("Error opening output: %s\n", argv[1]);
+
+    const char* out_name = argv[1];
+    int nfiles = argc - 2;
+
+    FILE* out = fopen(out_name, "wb");
+    if (!out) {
+        perror("fopen output");
         return 1;
     }
-    
-    // Write count
-    fwrite(&nheader, sizeof(uint32_t), 1, wstream);
-    // Write headers
-    fwrite(headers, sizeof(struct initrd_header), nheader, wstream);
-    
-    // Write data
-    for(int i = 0; i < nheader; i++) {
-        FILE *stream = fopen(argv[i+2], "rb");
-        unsigned char *buf = (unsigned char *)malloc(headers[i].length);
-        fread(buf, 1, headers[i].length, stream);
-        fwrite(buf, 1, headers[i].length, wstream);
-        fclose(stream);
-        free(buf);
+
+    printf("Creating InitRD (TAR USTAR) with %d files...\n", nfiles);
+
+    for (int i = 0; i < nfiles; i++) {
+        char src[256];
+        char dest[256];
+
+        const char* arg = argv[i + 2];
+        if (split_src_dest(arg, src, sizeof(src), dest, sizeof(dest))) {
+            // ok
+        } else {
+            strncpy(src, arg, sizeof(src) - 1);
+            src[sizeof(src) - 1] = 0;
+
+            const char* base = strrchr(arg, '/');
+            base = base ? base + 1 : arg;
+            strncpy(dest, base, sizeof(dest) - 1);
+            dest[sizeof(dest) - 1] = 0;
+        }
+
+        printf("Adding: %s -> %s\n", src, dest);
+
+        FILE* in = fopen(src, "rb");
+        if (!in) {
+            perror("fopen input");
+            fclose(out);
+            return 1;
+        }
+
+        fseek(in, 0, SEEK_END);
+        long len = ftell(in);
+        fseek(in, 0, SEEK_SET);
+        if (len < 0) {
+            fclose(in);
+            fclose(out);
+            return 1;
+        }
+
+        tar_write_header(out, dest, (uint32_t)len, '0');
+
+        uint8_t buf[4096];
+        long remaining = len;
+        while (remaining > 0) {
+            size_t chunk = (size_t)remaining;
+            if (chunk > sizeof(buf)) chunk = sizeof(buf);
+            size_t rd = fread(buf, 1, chunk, in);
+            if (rd != chunk) {
+                fclose(in);
+                fclose(out);
+                return 1;
+            }
+            fwrite(buf, 1, rd, out);
+            remaining -= (long)rd;
+        }
+
+        fclose(in);
+
+        // pad file to 512
+        uint32_t pad = (uint32_t)((TAR_BLOCK - ((uint32_t)len % TAR_BLOCK)) % TAR_BLOCK);
+        if (pad) write_zeros(out, pad);
     }
-    
-    fclose(wstream);
-    printf("Done. InitRD size: %d bytes.\n", data_offset);
-    
+
+    // Two zero blocks end-of-archive
+    write_zeros(out, TAR_BLOCK * 2);
+
+    long end = ftell(out);
+    fclose(out);
+
+    printf("Done. InitRD size: %ld bytes.\n", end);
     return 0;
 }
index 349b16bc6d161124edbec8c1b0d408b709a7f8f8..34dd58b9ce0cba34a4d236c70d03b8b2a22e773a 100644 (file)
@@ -76,10 +76,10 @@ void _start(void) {
     static const char msg[] = "[init] hello from init.elf\n";
     (void)sys_write(1, msg, (uint32_t)(sizeof(msg) - 1));
 
-    static const char path[] = "/init.elf";
+    static const char path[] = "/bin/init.elf";
     int fd = sys_open(path, 0);
     if (fd < 0) {
-        static const char emsg[] = "[init] open(/init.elf) failed\n";
+        static const char emsg[] = "[init] open(/bin/init.elf) failed\n";
         (void)sys_write(1, emsg, (uint32_t)(sizeof(emsg) - 1));
         sys_exit(1);
     }