#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];
}
#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;
}