rumpk/core/fs/tar.nim

197 lines
5.0 KiB
Nim

# MARKUS MAIWALD (ARCHITECT) | VOXIS FORGE (AI)
# Rumpk L1: Sovereign VFS (Indexing TarFS)
{.push stackTrace: off, lineTrace: off.}
import std/tables
# Kernel Imports (Avoid circular dependency)
proc kprint(s: cstring) {.importc, cdecl.}
proc kprintln(s: cstring) {.importc, cdecl.}
proc kprint_hex(n: uint64) {.importc, cdecl.} # Assuming this exists or I need to implement it equivalent
# --- TAR HEADER DEF (USTAR) ---
type
TarHeader* {.packed.} = object
name*: array[100, char]
mode*: array[8, char]
uid*: array[8, char]
gid*: array[8, char]
size*: array[12, char]
mtime*: array[12, char]
chksum*: array[8, char]
typeflag*: char
linkname*: array[100, char]
magic*: array[6, char]
version*: array[2, char]
uname*: array[32, char]
gname*: array[32, char]
devmajor*: array[8, char]
devminor*: array[8, char]
prefix*: array[155, char]
# Padding to 512 handled by jump logic
FileEntry = object
offset*: uint64
size*: uint64
FileHandle = object
path*: string
offset*: uint64
VFSInitRD* = object
start_addr*: uint64
end_addr*: uint64
index*: Table[string, FileEntry]
fds*: Table[int, FileHandle]
next_fd*: int
var vfs*: VFSInitRD
# --- HELPERS ---
proc parse_octal(a: openArray[char]): uint64 =
var res: uint64 = 0
var started = false
for c in a:
if c >= '0' and c <= '7':
res = (res shl 3) or (uint64(c) - uint64('0'))
started = true
elif started:
break
return res
proc align_512(n: uint64): uint64 =
if (n and 511) != 0:
return (n + 512) and not 511'u64
return n
proc cstring_to_nim(ptr_char: ptr char, max_len: int): string =
var res = newStringOfCap(max_len)
var p = ptr_char
var i = 0
while i < max_len and p[] != '\0':
res.add(p[])
p = cast[ptr char](cast[uint64](p) + 1)
i += 1
return res
# --- API ---
proc vfs_init*(s: pointer, e: pointer) =
vfs.start_addr = cast[uint64](s)
vfs.end_addr = cast[uint64](e)
vfs.index = initTable[string, FileEntry]()
vfs.fds = initTable[int, FileHandle]()
vfs.next_fd = 3 # 0,1,2 reserved
kprint("[VFS] Mounting TarFS InitRD... Start=")
kprint_hex(vfs.start_addr)
kprint(" End=")
kprint_hex(vfs.end_addr)
kprint(" Size=")
kprint_hex(vfs.end_addr - vfs.start_addr)
kprintln("")
var ptr_curr = vfs.start_addr
while ptr_curr < vfs.end_addr:
let header = cast[ptr TarHeader](ptr_curr)
# Check bounds safety before reading header
if ptr_curr + 512 > vfs.end_addr: break
# Check End of Archive (Empty Block)
if header.name[0] == '\0': break
let fname = cstring_to_nim(addr header.name[0], 100)
let size = parse_octal(header.size)
discard parse_octal(header.mode)
var clean_name = fname
if clean_name.len >= 2 and clean_name[0] == '.' and clean_name[1] == '/':
clean_name = clean_name[2..^1]
# Index Files (Type '0' or '\0')
if header.typeflag == '0' or header.typeflag == '\0':
if clean_name.len > 0:
let data_offset = ptr_curr + 512
vfs.index[clean_name] = FileEntry(offset: data_offset, size: size)
# Jump to next header
ptr_curr += 512 + align_512(size)
proc vfs_open*(path: string): int =
if vfs.index.hasKey(path):
let fd = vfs.next_fd
vfs.fds[fd] = FileHandle(path: path, offset: 0)
vfs.next_fd += 1
return fd
return -1
proc vfs_get_entry*(path: string): (pointer, uint64) =
if vfs.index.hasKey(path):
let entry = vfs.index[path]
return (cast[pointer](entry.offset), entry.size)
return (nil, 0)
# --- C EXPORTS (THE BRIDGE) ---
proc ion_vfs_open*(path: cstring): int32 {.exportc, cdecl.} =
return int32(vfs_open($path))
proc ion_vfs_read*(fd: int32, buf: pointer, count: uint64): int64 {.exportc, cdecl.} =
let fd_int = int(fd)
if not vfs.fds.hasKey(fd_int): return -1
var fh = vfs.fds[fd_int] # Get a mutable copy of the FileHandle
let (base_ptr, total_size) = vfs_get_entry(fh.path)
if base_ptr == nil: return 0
if fh.offset >= total_size: return 0 # EOF
# Calculate remaining bytes
let remaining = total_size - fh.offset
let to_read = min(uint64(remaining), count)
if to_read > 0:
let data_ptr = cast[pointer](cast[uint64](base_ptr) + fh.offset)
copyMem(buf, data_ptr, to_read)
# Update cursor
fh.offset += to_read
vfs.fds[fd_int] = fh # Write back to table
return int64(to_read)
return 0
proc vfs_list_files*(): string =
var res = ""
for name, entry in vfs.index:
res.add(name)
res.add("\n")
return res
proc ion_vfs_list*(buf: pointer, max_len: uint64): int64 {.exportc, cdecl.} =
let list = vfs_list_files()
let len = min(list.len, int(max_len))
if len > 0:
copyMem(buf, unsafeAddr list[0], len)
return int64(len)
proc vfs_read_file*(path: string): string =
if vfs.index.hasKey(path):
let entry = vfs.index[path]
let ptr_data = cast[ptr UncheckedArray[char]](entry.offset)
var s = newString(entry.size)
if entry.size > 0:
copyMem(addr s[0], ptr_data, entry.size)
return s
return ""
{.pop.}