Added lookup Tables

Simplified Inode Data types & union
Started work on decompression
This commit is contained in:
Caleb J. Gardner
2026-09-02 10:53:01 -05:00
parent 173e76469f
commit 2e33e19cdd
5 changed files with 373 additions and 107 deletions
+3 -2
View File
@@ -43,10 +43,11 @@ pub fn root(self: *Archive, alloc: std.mem.Allocator) !File {
.data = self.map.memory,
.super = self.super,
.inode = try .fromRef(
.inode = try .readLocation(
alloc,
self.map.memory,
self.root_inode_ref,
self.root_inode_ref.start,
self.root_inode_ref.offset,
self.super,
),
.name = "",
+140 -103
View File
@@ -11,9 +11,9 @@ const Inode = @This();
header: Header,
data: Data,
pub fn fromRef(alloc: std.mem.Allocator, data: []u8, ref: Reference, super: Super) !Inode {
var meta: MetadataReader = .init(alloc, data[super.inode_table_start + ref.start ..], super.decomp_fn);
try meta.interface.discardAll(ref.offset);
pub fn readLocation(alloc: std.mem.Allocator, data: []u8, start: u32, offset: u16, super: Super) !Inode {
var meta: MetadataReader = .init(alloc, data[super.inode_table_start + start ..], super.decomp_fn);
try meta.interface.discardAll(offset);
return read(alloc, &meta.interface, super.block_size);
}
@@ -23,20 +23,15 @@ pub fn read(alloc: std.mem.Allocator, rdr: *Io.Reader, block_size: u32) !Inode {
return .{
.header = hdr,
.data = switch (hdr.type) {
.dir => .{ .dir = try .read(rdr) },
.file => .{ .file = try .read(rdr, alloc, block_size) },
.symlink => .{ .symlink = try .read(rdr, alloc) },
.block_dev => .{ .block_dev = try .read(rdr) },
.char_dev => .{ .char_dev = try .read(rdr) },
.fifo => .{ .fifo = try .read(rdr) },
.socket => .{ .socket = try .read(rdr) },
.ext_dir => .{ .ext_dir = try .read(rdr) },
.ext_file => .{ .ext_file = try .read(rdr, alloc, block_size) },
.ext_symlink => .{ .ext_symlink = try .read(rdr, alloc) },
.ext_block_dev => .{ .ext_block_dev = try .read(rdr) },
.ext_char_dev => .{ .ext_char_dev = try .read(rdr) },
.ext_fifo => .{ .ext_fifo = try .read(rdr) },
.ext_socket => .{ .ext_socket = try .read(rdr) },
.dir => .{ .dir = try .readBasic(rdr) },
.ext_dir => .{ .dir = try .readExt(rdr) },
.file => .{ .file = try .readBasic(rdr, alloc, block_size) },
.ext_file => .{ .file = try .readExt(rdr, alloc, block_size) },
.symlink, .ext_symlink => .{ .symlink = try .read(rdr, alloc) },
.block_dev, .char_dev => .{ .dev = try .readBasic(rdr) },
.ext_block_dev, .ext_char_dev => .{ .dev = try .readExt(rdr) },
.fifo, .socket => .{ .fifo = try .readBasic(rdr) },
.ext_fifo, .ext_socket => .{ .fifo = try .readExt(rdr) },
},
};
}
@@ -56,9 +51,7 @@ pub fn copy(self: Inode, alloc: std.mem.Allocator) !Inode {
pub fn deinit(self: Inode, alloc: std.mem.Allocator) void {
switch (self.data) {
.file => |f| alloc.free(f.blocks),
.ext_file => |f| alloc.free(f.blocks),
.symlink => |s| alloc.free(s.target),
.ext_symlink => |s| alloc.free(s.target),
else => {},
}
}
@@ -103,111 +96,135 @@ pub const Header = extern struct {
inode_num: u32,
};
pub const Data = union(Type) {
pub const Data = union(enum) {
dir: Dir,
file: File,
symlink: Symlink,
block_dev: Dev,
char_dev: Dev,
dev: Dev,
fifo: Fifo,
socket: Fifo,
ext_dir: ExtDir,
ext_file: ExtFile,
ext_symlink: Symlink,
ext_block_dev: ExtDev,
ext_char_dev: ExtDev,
ext_fifo: ExtFifo,
ext_socket: ExtFifo,
};
// Inode data types
pub const Dir = extern struct {
start: u32,
hard_links: u32,
size: u16,
offset: u16,
parent_inode_num: u32,
pub const Dir = struct {
//RegularDir structure:
// start: u32,
// hard_links: u32,
// size: u16,
// offset: u16,
// parent_inode_num: u32,
// ExtDir structure
// hard_links: u32,
// size: u32,
// start: u32,
// parent_inode_num: u32,
// idx_count: u16,
// offset: u16,
// xattr_idx: u32,
// dir_indexes: []DirIndex,
pub fn read(rdr: *Io.Reader) !Dir {
return util.readValueRdr(Dir, rdr);
}
};
pub const ExtDir = extern struct {
hard_links: u32,
size: u32,
start: u32,
parent_inode_num: u32,
idx_count: u16,
offset: u16,
xattr_idx: u32,
xattr_idx: ?u32,
pub fn read(rdr: *Io.Reader) !ExtDir {
return util.readValueRdr(ExtDir, rdr);
pub fn readBasic(rdr: *Io.Reader) !Dir {
var raw: [12]u8 = undefined;
try rdr.readSliceAll(&raw);
return .{
.start = util.readValue(u32, raw[0..4]),
.hard_links = util.readValue(u32, raw[4..8]),
.size = util.readValue(u16, raw[8..10]),
.offset = util.readValue(u16, raw[10..12]),
.xattr_idx = null,
};
}
pub fn readExt(rdr: *Io.Reader) !Dir {
var raw: [24]u8 = undefined;
try rdr.readSliceAll(&raw);
return .{
.hard_links = util.readValue(u32, raw[0..4]),
.size = util.readValue(u32, raw[4..8]),
.start = util.readValue(u32, raw[8..12]),
.offset = util.readValue(u16, raw[18..20]),
.xattr_idx = util.readValue(u32, raw[20..24]),
};
}
};
pub const File = struct {
start: u32,
//RegularFile structure:
// start: u32,
// frag_idx: u32,
// frag_offset: u32,
// size: u32,
// block_sizes: []BlockSize,
//ExtFile structure:
// start: u64,
// size: u64,
// sparse: u64,
// hard_links: u32,
// frag_idx: u32,
// frag_offset: u32,
// xattr_idx: u32,
// block_sizes: []BlockSize,
start: u64,
frag_idx: u32,
frag_offset: u32,
size: u32,
size: u64,
blocks: []BlockSize,
xattr_idx: ?u32,
pub fn read(rdr: *Io.Reader, alloc: std.mem.Allocator, block_size: u32) !File {
pub fn readBasic(rdr: *Io.Reader, alloc: std.mem.Allocator, block_size: u32) !File {
var raw: [16]u8 = undefined;
try rdr.readSliceAll(&raw);
const frag_idx = std.mem.readInt(u32, raw[4..8], .little);
const size = std.mem.readInt(u32, raw[12..16], .little);
const frag_idx = util.readValue(u32, raw[4..8]);
const size = util.readValue(u32, raw[12..16]);
var num = size / block_size;
if (frag_idx == 0xFFFFFFFF and size % block_size > 0)
num += 1;
const block_num = if (frag_idx == 0xFFFFFFFF)
try std.math.divCeil(usize, size, block_size)
else
size / block_size;
const blocks = try alloc.alloc(BlockSize, num);
try rdr.readSliceEndian(BlockSize, blocks, .little);
const sizes = try alloc.alloc(BlockSize, block_num);
try rdr.readSliceEndian(BlockSize, sizes, .little);
return .{
.start = std.mem.readInt(u32, raw[0..4], .little),
.start = util.readValue(u32, raw[0..4]),
.frag_idx = frag_idx,
.frag_offset = std.mem.readInt(u32, raw[8..12], .little),
.frag_offset = util.readValue(u32, raw[8..12]),
.size = size,
.blocks = blocks,
.blocks = sizes,
.xattr_idx = null,
};
}
};
pub const ExtFile = struct {
start: u64,
size: u64,
hard_links: u32,
frag_idx: u32,
frag_offset: u32,
xattr_idx: u32,
blocks: []BlockSize,
pub fn read(rdr: *Io.Reader, alloc: std.mem.Allocator, block_size: u32) !ExtFile {
pub fn readExt(rdr: *Io.Reader, alloc: std.mem.Allocator, block_size: u32) !File {
var raw: [40]u8 = undefined;
try rdr.readSliceAll(&raw);
const frag_idx = std.mem.readInt(u32, raw[28..32], .little);
const size = std.mem.readInt(u64, raw[8..16], .little);
const frag_idx = util.readValue(u32, raw[28..32]);
const size = util.readValue(u64, raw[8..16]);
var num = size / block_size;
if (frag_idx == 0xFFFFFFFF and size % block_size > 0)
num += 1;
const block_num = if (frag_idx == 0xFFFFFFFF)
try std.math.divCeil(usize, size, block_size)
else
size / block_size;
const blocks = try alloc.alloc(BlockSize, num);
try rdr.readSliceEndian(BlockSize, blocks, .little);
const sizes = try alloc.alloc(BlockSize, block_num);
try rdr.readSliceEndian(BlockSize, sizes, .little);
return .{
.start = std.mem.readInt(u64, raw[0..8], .little),
.start = util.readValue(u64, raw[0..8]),
.size = size,
.hard_links = std.mem.readInt(u32, raw[24..28], .little),
.frag_idx = frag_idx,
.frag_offset = std.mem.readInt(u32, raw[32..36], .little),
.xattr_idx = std.mem.readInt(u32, raw[36..], .little),
.blocks = blocks,
.frag_offset = util.readValue(u32, raw[32..36]),
.blocks = sizes,
.xattr_idx = util.readValue(u32, raw[36..40]),
};
}
};
@@ -215,6 +232,7 @@ pub const ExtFile = struct {
pub const Symlink = struct {
hard_links: u32,
target: []const u8,
// xattr_idx is ignored on extended symlinks as you can't apply them to symlinks anyway.
pub fn read(rdr: *Io.Reader, alloc: std.mem.Allocator) !Symlink {
var raw: [8]u8 = undefined;
@@ -232,36 +250,55 @@ pub const Symlink = struct {
}
};
pub const Dev = extern struct {
pub const Dev = struct {
hard_links: u32,
device: u32,
// xattr_idx omitted on basic device inodes.
xattr_idx: ?u32,
pub fn read(rdr: *Io.Reader) !Dev {
return util.readValueRdr(Dev, rdr);
pub fn readBasic(rdr: *Io.Reader) !Dev {
var raw: [8]u8 = undefined;
try rdr.readSliceAll(&raw);
return .{
.hard_links = util.readValue(u32, raw[0..4]),
.device = util.readValue(u32, raw[4..8]),
.xattr_idx = null,
};
}
pub fn readExt(rdr: *Io.Reader) !Dev {
var raw: [12]u8 = undefined;
try rdr.readSliceAll(&raw);
return .{
.hard_links = util.readValue(u32, raw[0..4]),
.device = util.readValue(u32, raw[4..8]),
.xattr_idx = util.readValue(u32, raw[8..12]),
};
}
};
pub const ExtDev = extern struct {
pub const Fifo = struct {
hard_links: u32,
device: u32,
xattr_idx: u32,
// Xattr_idx omitted on basic fifo inodes.
xattr_idx: ?u32,
pub fn read(rdr: *Io.Reader) !ExtDev {
return util.readValueRdr(ExtDev, rdr);
}
};
pub const Fifo = extern struct {
hard_links: u32,
pub fn readBasic(rdr: *Io.Reader) !Fifo {
var raw: [4]u8 = undefined;
try rdr.readSliceAll(&raw);
pub fn read(rdr: *Io.Reader) !Fifo {
return util.readValueRdr(Fifo, rdr);
return .{
.hard_links = util.readValue(u32, raw[0..4]),
.xattr_idx = null,
};
}
};
pub const ExtFifo = extern struct {
hard_links: u32,
xattr_idx: u32,
pub fn readExt(rdr: *Io.Reader) !Fifo {
var raw: [8]u8 = undefined;
try rdr.readSliceAll(&raw);
pub fn read(rdr: *Io.Reader) !ExtFifo {
return util.readValueRdr(ExtFifo, rdr);
return .{
.hard_links = util.readValue(u32, raw[0..4]),
.xattr_idx = util.readValue(u32, raw[4..8]),
};
}
};
+80
View File
@@ -0,0 +1,80 @@
const std = @import("std");
const Io = std.Io;
const util = @import("utils/util.zig");
const MetadataReader = @import("utils/meta.zig");
const Decomp = @import("decomp.zig");
pub fn Table(comptime T: type) type {
return struct {
const Self = @This();
const ITEMS_PER_BLOCK = 8192 / @sizeOf(T);
alloc: std.mem.Allocator,
data: []u8,
decomp: Decomp.Fn,
start: u64,
count: u32,
block_count: u32,
table: std.hash_map.AutoHashMapUnmanaged(u32, []T) = .empty,
mut: Io.Mutex = .init,
pub fn init(alloc: std.mem.Allocator, data: []u8, decomp: Decomp.Fn, start: u64, count: u32) !Self {
return .{
.alloc = alloc,
.data = data,
.decomp = decomp,
.start = start,
.count = count,
.block_count = std.math.divCeil(u32, count, ITEMS_PER_BLOCK),
};
}
pub fn deinit(self: *Self) void {
var iter = self.table.valueIterator();
while (iter.next()) |block|
self.alloc.free(block);
self.table.deinit(self.alloc);
}
fn getBlock(self: *Self, io: Io, idx: u32) ![]T {
const init_get = self.table.get(idx);
if (init_get != null) return init_get.?;
self.mut.lock(io);
defer self.mut.unlock(io);
const start = util.readValue(u64, self.data[self.start + (idx * 8) ..][0..8]);
const len = if (idx == self.block_count - 1) self.count % ITEMS_PER_BLOCK else ITEMS_PER_BLOCK;
const new_block = try self.alloc.alloc(T, len);
errdefer self.alloc.free(new_block);
var meta: MetadataReader = .init(self.alloc, self.data[start..], self.decomp);
try meta.interface.readSliceEndian(T, new_block, .little);
try self.table.put(self.alloc, idx, new_block);
return new_block;
}
pub fn get(self: *Self, io: Io, idx: u32) !T {
if (idx >= self.count) return error.InvalidIndex;
const block_idx = idx / ITEMS_PER_BLOCK;
const block = self.getBlock(io, block_idx);
return block[idx % ITEMS_PER_BLOCK];
}
};
}
+2 -2
View File
@@ -31,11 +31,11 @@ interface: Io.Reader = .{
},
},
pub fn init(alloc: std.mem.Allocator, data: []u8, decomp: Decomp.Fn, block_size: u32, start: u64, size: u64, blocks: []BlockSize) DataReader {
pub fn init(alloc: std.mem.Allocator, data: []u8, decomp: Decomp.Fn, block_size: u32, size: u64, blocks: []BlockSize) DataReader {
return .{
.alloc = alloc,
.data = data[start..],
.data = data,
.decomp = decomp,
.block_size = block_size,
+148
View File
@@ -0,0 +1,148 @@
const std = @import("std");
const Io = std.Io;
const Options = @import("../options.zig");
const Inode = @import("../inode.zig");
const Decomp = @import("../decomp.zig");
const Directory = @import("../dir.zig");
const MetadataReader = @import("meta.zig");
const DataReader = @import("data_reader.zig");
const Super = @import("../archive.zig").Super;
pub fn single(alloc: std.mem.Allocator, io: Io, data: []const u8, decomp: Decomp.Fn, super: Super, inode: Inode, filepath: []const u8, options: Options) !void {
const path = std.mem.trim(u8, filepath, "/");
var pool: std.ArrayList(InodeAndPath) = .initBuffer(&.{.{ .inode = inode, .path = path }});
defer {
while (pool.pop()) |in|
in.deinit(in);
pool.deinit(alloc);
}
var dirs: std.ArrayList(InodeAndPath) = .empty;
defer {
while (dirs.pop()) |dir|
alloc.free(dir.path);
dirs.deinit(alloc);
}
// TODO: Use options
_ = options;
while (pool.pop()) |in| {
switch (in.inode.data) {
.dir => |d| {
try Io.Dir.cwd().createDirPath(io, in.path);
if (d.size <= 3) {
defer if (in.path.len != path.len)
in.deinit(alloc);
// TODO: apply permissions & xattrs immediately.
continue;
}
var meta: MetadataReader = .init(alloc, data[d.start], decomp);
try meta.interface.discardAll(d.offset);
var dir: Directory = .read(alloc, &meta.interface, d.size);
defer dir.deinit(alloc);
for (dir.entries) |entry| {
const new_inode: Inode = try .readLocation(alloc, data, entry.start, entry.offset, super);
const new_path = std.mem.concat(alloc, u8, &.{ in.path, "/", entry.name }) catch |err| {
new_inode.deinit(alloc);
return err;
};
try pool.append(alloc, .{ .inode = new_inode, .path = new_path });
}
try dirs.append(alloc, in);
},
.file => |f| {
defer if (in.path.len != path.len)
in.deinit(alloc);
var atomic = try Io.Dir.cwd().createFileAtomic(io, in.path, .{});
defer atomic.deinit(io);
var rdr: DataReader = .init(alloc, data[f.start..], decomp, super.block_size, f.size, f.blocks);
defer rdr.deinit();
if (f.frag_idx != 0xFFFFFFFF) {
//TODO: fragment
}
var writer = atomic.file.writer(io, &[0]u8{}); // TODO: find a resonable buffer size.
try rdr.interface.streamRemaining(&writer.interface);
// TODO: apply permissions & xattrs
try atomic.link(io);
},
.symlink => |s| {
defer if (in.path.len != path.len)
in.deinit(alloc);
try Io.Dir.cwd().symLink(io, s.target, in.path, .{});
},
.dev => |d| {
defer if (in.path.len != path.len)
in.deinit(alloc);
const mode = switch (in.inode.header.type) {
.char_dev, .ext_char_dev => std.os.linux.DT.CHR,
.block_dev, .ext_block_dev => std.os.linux.DT.BLK,
else => unreachable,
};
const sent_path = try alloc.dupeSentinel(u8, in.path, 0);
const res = std.os.linux.mknod(sent_path, mode, d.device);
alloc.free(sent_path);
if (res != 0)
return error.MkNodError;
// TODO: apply permissions & xattrs
},
.fifo => |f| {
_ = f;
defer if (in.path.len != path.len)
in.deinit(alloc);
const mode = switch (in.inode.header.type) {
.fifo, .ext_fifo => std.os.linux.DT.FIFO,
.socket, .ext_socket => std.os.linux.DT.SOCK,
else => unreachable,
};
const sent_path = try alloc.dupeSentinel(u8, in.path, 0);
const res = std.os.linux.mknod(sent_path, mode, 0);
alloc.free(sent_path);
if (res != 0)
return error.MkNodError;
// TODO: apply permissions & xattrs
},
}
}
while (dirs.pop()) |dir| {
defer if (dir.path.len != path.len)
alloc.free(dir.path);
// TODO: apply permissions & xattrs
}
}
const InodeAndPath = struct {
inode: Inode,
path: []const u8,
fn deinit(self: InodeAndPath, alloc: std.mem.Allocator) void {
self.inode.deinit(alloc);
alloc.free(self.path);
}
};
pub fn multi() !void {}