From: Tulio A M Mendes Date: Sat, 7 Feb 2026 18:45:01 +0000 (-0300) Subject: initrd: switch to TAR USTAR with directory support X-Git-Url: https://projects.tadryanom.me/?a=commitdiff_plain;h=06da4493b803521afeb8ef8aba9385c5dd57678d;p=AdrOS.git initrd: switch to TAR USTAR with directory support --- diff --git a/Makefile b/Makefile index 59d22ce..22be95f 100644 --- 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 diff --git a/include/initrd.h b/include/initrd.h index 1e8c1e2..8dbb301 100644 --- a/include/initrd.h +++ b/include/initrd.h @@ -4,13 +4,6 @@ #include "fs.h" #include -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); diff --git a/src/arch/x86/arch_platform.c b/src/arch/x86/arch_platform.c index 5338daf..757356e 100644 --- a/src/arch/x86/arch_platform.c +++ b/src/arch/x86/arch_platform.c @@ -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'); diff --git a/src/drivers/initrd.c b/src/drivers/initrd.c index aa545bd..e339e2a 100644 --- a/src/drivers/initrd.c +++ b/src/drivers/initrd.c @@ -3,77 +3,298 @@ #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]; } diff --git a/tools/mkinitrd.c b/tools/mkinitrd.c index af062e5..108fdd2 100644 --- a/tools/mkinitrd.c +++ b/tools/mkinitrd.c @@ -1,76 +1,191 @@ #include #include -#include #include +#include + +#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 [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; } diff --git a/user/init.c b/user/init.c index 349b16b..34dd58b 100644 --- a/user/init.c +++ b/user/init.c @@ -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); }