From e0b697dd0bf789f72e2dceddbed971962002a9e0 Mon Sep 17 00:00:00 2001 From: "Caleb J. Gardner" Date: Fri, 14 Aug 2026 17:17:22 -0500 Subject: [PATCH] More work, specifically on decompression --- src/archive.zig | 50 +++++++++++++++++---- src/bin/unsquashfs.zig | 12 ++++- src/c.h | 4 +- src/decomp.zig | 100 +++++++++++++++++++++++++++++++++++++++++ src/inode.zig | 4 +- src/tests.zig | 2 + src/util.zig | 16 ------- src/utils/meta.zig | 8 ++++ src/utils/util.zig | 22 +++++++++ 9 files changed, 189 insertions(+), 29 deletions(-) create mode 100644 src/decomp.zig delete mode 100644 src/util.zig create mode 100644 src/utils/meta.zig create mode 100644 src/utils/util.zig diff --git a/src/archive.zig b/src/archive.zig index 0431c54..ac5b6f4 100644 --- a/src/archive.zig +++ b/src/archive.zig @@ -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, +}; diff --git a/src/bin/unsquashfs.zig b/src/bin/unsquashfs.zig index 7faaa5a..d56595a 100644 --- a/src/bin/unsquashfs.zig +++ b/src/bin/unsquashfs.zig @@ -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); } diff --git a/src/c.h b/src/c.h index 22bb050..258f19f 100644 --- a/src/c.h +++ b/src/c.h @@ -1,7 +1,7 @@ -#include -#include #include #include +#include +#include #ifdef ALLOW_LZO #include diff --git a/src/decomp.zig b/src/decomp.zig new file mode 100644 index 0000000..f4ccc82 --- /dev/null +++ b/src/decomp.zig @@ -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; +} diff --git a/src/inode.zig b/src/inode.zig index 6b33594..8eb5100 100644 --- a/src/inode.zig +++ b/src/inode.zig @@ -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) { diff --git a/src/tests.zig b/src/tests.zig index 5dedc13..939b071 100644 --- a/src/tests.zig +++ b/src/tests.zig @@ -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(); } diff --git a/src/util.zig b/src/util.zig deleted file mode 100644 index a5e3a11..0000000 --- a/src/util.zig +++ /dev/null @@ -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; -} diff --git a/src/utils/meta.zig b/src/utils/meta.zig new file mode 100644 index 0000000..3881227 --- /dev/null +++ b/src/utils/meta.zig @@ -0,0 +1,8 @@ +const std = @import("std"); + +const Decomp = @import("../decomp.zig"); + +const MetadataReader = @This(); + +data: []u8, +decomp: Decomp.Fn, diff --git a/src/utils/util.zig b/src/utils/util.zig new file mode 100644 index 0000000..c8e34e8 --- /dev/null +++ b/src/utils/util.zig @@ -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; +}