More work, specifically on decompression

This commit is contained in:
Caleb J. Gardner
2026-08-14 17:17:22 -05:00
parent 9f2a4bcc1e
commit e0b697dd0b
9 changed files with 189 additions and 29 deletions
+42 -8
View File
@@ -3,13 +3,16 @@ const Io = std.Io;
const File = @import("file.zig");
const Inode = @import("inode.zig");
const Util = @import("util.zig");
const Util = @import("utils/util.zig");
const Decomp = @import("decomp.zig");
const Options = @import("options.zig");
const Archive = @This();
map: Io.File.MemoryMap,
super: Superblock,
super: Super,
pub fn open(io: Io, file: Io.File, offset: u64) !Archive {
var map = try file.createMemoryMap(io, .{
@@ -18,8 +21,8 @@ pub fn open(io: Io, file: Io.File, offset: u64) !Archive {
.protection = .{ .read = true },
});
var super = Util.readValue(Superblock, map.memory[0..@sizeOf(Superblock)]);
try super.check();
var superblock = Util.readValue(Superblock, map.memory[0..@sizeOf(Superblock)]);
const super = try superblock.check();
return .{
.map = map,
@@ -35,14 +38,18 @@ pub fn root(self: *Archive) !File {
_ = self;
return error.TODO;
}
pub fn extract(self: *Archive) !void {
pub fn extract(self: *Archive, alloc: std.mem.Allocator, io: Io, ext_loc: []const u8, options: Options) !void {
_ = self;
_ = alloc;
_ = io;
_ = ext_loc;
_ = options;
return error.TODO;
}
// Superblock
pub const Superblock = extern struct {
const Superblock = extern struct {
pub const MAGIC: u32 = 0x73717368;
magic: u32,
@@ -50,7 +57,7 @@ pub const Superblock = extern struct {
mod_time: u32,
block_size: u32,
frag_count: u32,
compression: u16,
compression: Decomp.Enum,
block_log: u16,
flags: u16, // TODO: break out into packed struct.
id_count: u16,
@@ -65,12 +72,39 @@ pub const Superblock = extern struct {
frag_table_start: u64,
export_table_start: u64,
fn check(self: Superblock) !void {
fn check(self: Superblock) !Super {
if (self.magic != MAGIC)
return error.InvalidMagic;
if (self.version_major != 4 or self.version_minor != 0)
return error.IncompatibleVersion;
if (std.math.log2(self.block_size) != self.block_log)
return error.BadBlockLog;
return .{
.block_size = self.block_size,
.frag_count = self.frag_count,
.decomp_fn = try self.compression.func(),
.id_count = self.id_count,
.root_inode_ref = self.root_inode_ref,
.id_table_start = self.id_table_start,
.xattr_table_start = self.xattr_table_start,
.inode_table_start = self.inode_table_start,
.dir_table_start = self.dir_table_start,
.frag_table_start = self.frag_table_start,
.export_table_start = self.export_table_start,
};
}
};
pub const Super = struct {
block_size: u32,
frag_count: u32,
decomp_fn: Decomp.Fn,
id_count: u16,
root_inode_ref: Inode.Reference,
id_table_start: u64,
xattr_table_start: u64,
inode_table_start: u64,
dir_table_start: u64,
frag_table_start: u64,
export_table_start: u64,
};
+11 -1
View File
@@ -4,6 +4,8 @@ const Writer = Io.Writer;
const config = @import("build_config");
const squashfs = @import("squashfs");
const Archive = squashfs.Archive;
const Options = squashfs.Options;
//TODO: Add more options
const help_mgs =
@@ -34,7 +36,7 @@ var offset: u64 = 0;
var threads: usize = 0;
var force: bool = false;
var options: squashfs.Options = .default;
var options: Options = .default;
pub fn main(init: std.process.Init) !void {
var io = init.io;
@@ -55,4 +57,12 @@ pub fn main(init: std.process.Init) !void {
});
io = limited_io.io();
}
var fil = try Io.Dir.cwd().openFile(io, arc_loc, .{});
defer fil.close(io);
var arc: Archive = try .open(io, fil, offset);
defer arc.close(io);
try arc.extract(alloc, io, ext_loc, options);
}
+2 -2
View File
@@ -1,7 +1,7 @@
#include <zlib-ng.h>
#include <zstd.h>
#include <lz4.h>
#include <lzma.h>
#include <zlib.h>
#include <zstd.h>
#ifdef ALLOW_LZO
#include <lzo/minilzo.h>
+100
View File
@@ -0,0 +1,100 @@
const std = @import("std");
const Io = std.Io;
const c = @import("c");
const build = @import("build_config");
pub const Enum = enum(u16) {
gzip = 1,
lzma,
lzo,
xz,
lz4,
zstd,
pub fn func(self: Enum) !Fn {
return switch (self) {
.gzip => if (build.zig_decomp) zigZlib else cZlib,
.lzma => if (build.zig_decomp) zigLzma else cLzma,
.lzo => if (build.zig_decomp) error.LzoUnsupported else cLzo,
.xz => if (build.zig_decomp) zigXz else cXz,
.lz4 => if (build.zig_decomp) error.Lz4Unsupported else cLz4,
.zstd => if (build.zig_decomp) zigZstd else cZstd,
};
}
};
pub const Fn = *const fn (alloc: std.mem.Allocator, in: []u8, out: []u8) Error!usize;
pub const Error = Io.Reader.Error;
// Actual decomp functions
fn zigZlib(_: std.mem.Allocator, in: []u8, out: []u8) Error!usize {
var rdr: Io.Reader = .fixed(in);
var decomp: std.compress.flate.Decompress = .init(&rdr, .zlib, &[0]u8{});
return decomp.reader.readSliceShort(out);
}
fn cZlib(_: std.mem.Allocator, in: []u8, out: []u8) Error!usize {
var stream: c.z_stream = .{
.avail_in = @truncate(in.len),
.next_in = in.ptr,
.avail_out = @truncate(out.len),
.next_out = out.ptr,
};
const res = c.inflate(&stream, c.Z_FULL_FLUSH);
if (res != c.Z_OK)
return Error.ReadFailed;
return 0;
}
fn zigLzma(alloc: std.mem.Allocator, in: []u8, out: []u8) Error!usize {
var rdr: Io.Reader = .fixed(in);
var decomp: std.compress.lzma.Decompress = .initOptions(&rdr, alloc, out, .{}, 0);
}
fn cLzma(alloc: std.mem.Allocator, in: []u8, out: []u8) Error!usize {
_ = alloc;
_ = in;
_ = out;
return Error.ReadFailed;
}
fn cLzo(alloc: std.mem.Allocator, in: []u8, out: []u8) Error!usize {
_ = alloc;
_ = in;
_ = out;
return Error.ReadFailed;
}
fn zigXz(alloc: std.mem.Allocator, in: []u8, out: []u8) Error!usize {
_ = alloc;
_ = in;
_ = out;
return Error.ReadFailed;
}
fn cXz(alloc: std.mem.Allocator, in: []u8, out: []u8) Error!usize {
_ = alloc;
_ = in;
_ = out;
return Error.ReadFailed;
}
fn cLz4(alloc: std.mem.Allocator, in: []u8, out: []u8) Error!usize {
_ = alloc;
_ = in;
_ = out;
return Error.ReadFailed;
}
fn zigZstd(alloc: std.mem.Allocator, in: []u8, out: []u8) Error!usize {
_ = alloc;
_ = in;
_ = out;
return Error.ReadFailed;
}
fn cZstd(alloc: std.mem.Allocator, in: []u8, out: []u8) Error!usize {
_ = alloc;
_ = in;
_ = out;
return Error.ReadFailed;
}
+2 -2
View File
@@ -19,9 +19,9 @@ pub fn deinit(self: Inode, alloc: std.mem.Allocator) void {
// Types
pub const Reference = packed struct(u64) {
_: u16,
block: u32,
offset: u16,
block: u32,
_: u16,
};
pub const Type = enum(u16) {
+2
View File
@@ -15,6 +15,8 @@ test "Open" {
var arc: Archive = try .open(io, archive_file, 0);
defer arc.close(io);
std.debug.print("{}\n", .{arc.super});
var root = try arc.root();
defer root.close();
}
-16
View File
@@ -1,16 +0,0 @@
const std = @import("std");
const builtin = @import("builtin");
pub fn readValue(comptime T: type, bytes: []u8) T {
var res = std.mem.bytesToValue(T, bytes);
if (builtin.cpu.arch.endian() != .little) {
switch (@typeInfo(T)) {
.@"enum" => res = std.mem.nativeToLittle(@typeInfo(T).@"enum".tag_type, res),
.@"struct" => std.mem.byteSwapAllFields(T, &res),
.int => res = std.mem.nativeToLittle(T, res),
.array => std.mem.byteSwapAllElements(@typeInfo(T).array.child, res),
else => unreachable,
}
}
return res;
}
+8
View File
@@ -0,0 +1,8 @@
const std = @import("std");
const Decomp = @import("../decomp.zig");
const MetadataReader = @This();
data: []u8,
decomp: Decomp.Fn,
+22
View File
@@ -0,0 +1,22 @@
const std = @import("std");
const builtin = @import("builtin");
pub fn readValue(comptime T: type, bytes: []u8) T {
var res = std.mem.bytesToValue(T, bytes);
if (comptime builtin.cpu.arch.endian() != .little) {
switch (@typeInfo(T)) {
.@"enum" => res = std.mem.nativeToLittle(@typeInfo(T).@"enum".tag_type, res),
.@"struct" => |s| {
if (s.layout == .@"packed") {
res = @bitCast(std.mem.nativeToLittle(s.backing_integer.?, @bitCast(res)));
} else {
std.mem.byteSwapAllFields(T, &res);
}
},
.int => res = std.mem.nativeToLittle(T, res),
.array => |a| std.mem.byteSwapAllElements(a.child, res),
else => unreachable,
}
}
return res;
}