Updated unsquashfs for zig 0.16.0

Fixed a couple bugs
Added scaffold for extraction
This commit is contained in:
Caleb Gardner
2026-05-29 18:50:45 -05:00
parent 2cb0863cc1
commit 56ad79ba94
10 changed files with 145 additions and 57 deletions
+7 -1
View File
@@ -56,6 +56,9 @@ pub fn build(b: *std.Build) !void {
b.installArtifact(lib);
const exe_config = b.addOptions();
exe_config.addOption(std.SemanticVersion,"version", version);
const exe = b.addExecutable(.{
.name = "unsquashfs",
.use_llvm = debug,
@@ -65,9 +68,12 @@ pub fn build(b: *std.Build) !void {
.target = target,
.root_source_file = b.path("src/bin/unsquashfs.zig"),
.valgrind = debug,
.imports = &.{
.{ .name = "config", .module = exe_config.createModule() },
.{ .name = "squashfs", .module = lib.root_module }
},
}),
});
exe.root_module.linkLibrary(lib);
b.installArtifact(exe);
+1 -1
View File
@@ -2,7 +2,7 @@
.name = .squashfs,
.version = "0.0.6",
.fingerprint = 0x37ba29474b87f145, // Changing this has security and trust implications.
.minimum_zig_version = "0.15.2",
.minimum_zig_version = "0.16.0",
.dependencies = .{
.zlib_ng = .{
.url = "git+https://github.com/CalebQ42/zig-zlib-ng#5f2f02dfb28acca2517dacbbd09e9b987f57b133",
+31 -13
View File
@@ -5,6 +5,7 @@ const MemoryMap = File.MemoryMap;
const Decomp = @import("decomp.zig");
const DecompCache = @import("decomp_cache.zig");
const Extract = @import("extract.zig");
const ExtractionOptions = @import("options.zig");
const Inode = @import("inode.zig");
const SfsFile = @import("file.zig");
@@ -36,7 +37,7 @@ pub fn initAdvanced(alloc: std.mem.Allocator, io: Io, fil: File, offset: u64, ca
return .{
.super = super,
.cache = .init(
.cache = try .init(
alloc,
map,
super.compression,
@@ -74,12 +75,15 @@ pub fn open(self: *Archive, alloc: std.mem.Allocator, io: Io, filepath: []const
}
pub fn extract(self: *Archive, alloc: std.mem.Allocator, io: Io, ext_loc: []const u8, options: ExtractionOptions) !void {
_ = self;
_ = alloc;
_ = io;
_ = ext_loc;
_ = options;
return error.TODO;
const root_inode: Inode = try .initRef(
alloc,
io,
&self.cache,
self.super.inode_start,
self.super.block_size,
self.super.root_ref,
);
return Extract.extract(alloc, io, root_inode, &self.cache, self.super, ext_loc, options);
}
// Superblock
@@ -93,7 +97,19 @@ pub const Superblock = extern struct {
compression: Decomp.Enum,
block_log: u16,
flags: packed struct(u16) {
TODO: u16,
inode_uncompressed: bool,
data_uncompressed: bool,
check: bool,
frag_uncompressed: bool,
frag_never: bool,
frag_always: bool,
de_dupe: bool,
exportable: bool,
xattr_uncompressed: bool,
xattr_never: bool,
compression_options: bool,
id_uncompressed: bool,
_: u4,
},
id_count: u16,
ver_maj: u16,
@@ -114,6 +130,8 @@ pub const Superblock = extern struct {
return error.InvalidVersion;
if (self.block_log != std.math.log2(self.block_size))
return error.BadBlockLog;
if (self.flags.check)
return error.BadCheckFlag;
}
};
@@ -127,10 +145,10 @@ test "Basics" {
var archive_file = try Io.Dir.cwd().openFile(io, TestArchive, .{});
defer archive_file.close(io);
var arc: Archive = try .init(io, archive_file);
var arc: Archive = try .init(alloc, io, archive_file);
defer arc.deinit(io);
var root_file = try arc.root(alloc);
var root_file = try arc.root(alloc, io);
defer root_file.deinit();
}
@@ -143,10 +161,10 @@ test "SingleFileExtraction" {
var archive_file = try Io.Dir.cwd().openFile(io, TestArchive, .{});
defer archive_file.close(io);
var arc: Archive = try .init(io, archive_file);
var arc: Archive = try .init(alloc, io, archive_file);
defer arc.deinit(io);
var ext_file = try arc.open(alloc, TestFile);
var ext_file = try arc.open(alloc, io, TestFile);
defer ext_file.deinit();
try ext_file.extract(alloc, io, TestFileExtractLocation, .default);
@@ -160,7 +178,7 @@ test "FullExtraction" {
var archive_file = try Io.Dir.cwd().openFile(io, TestArchive, .{});
defer archive_file.close(io);
var arc: Archive = try .init(io, archive_file);
var arc: Archive = try .init(alloc, io, archive_file);
defer arc.deinit(io);
try arc.extract(alloc, io, TestFullExtractLocation, .default);
+25 -22
View File
@@ -1,9 +1,11 @@
const std = @import("std");
const Writer = std.Io.Writer;
const Io = std.Io;
const Writer = Io.Writer;
const File = Io.File;
const builtin = @import("builtin");
const config = @import("config");
const squashfs = @import("zig_squashfs");
const squashfs = @import("squashfs");
//TODO: Add more options
const help_mgs =
@@ -38,40 +40,41 @@ var ignore_xattrs: bool = false;
var ignore_permissions: bool = false;
var force: bool = false;
pub fn main() !void {
const alloc = std.heap.smp_allocator;
var stdout = std.fs.File.stdout();
var out = stdout.writer(&[0]u8{});
pub fn main(init: std.process.Init) !void {
const alloc = init.gpa;
const io = init.io;
var stdout = File.stdout();
var out = stdout.writer(io, &[0]u8{});
defer out.interface.flush() catch {};
try handleArgs(alloc, &out.interface);
try handleArgs(init.minimal.args, &out.interface);
if (archive.len == 0) {
try out.interface.print("You must provide a squashfs archive\n", .{});
try out.interface.print(help_mgs, .{});
return;
}
var fil: std.fs.File = try std.fs.cwd().openFile(archive, .{}); //TODO: Handle error gracefully.
defer fil.close();
var arc: squashfs.Archive = try .init(alloc, fil, offset); //TODO: Update when memory size matters. //TODO: Handle error gracefully.
defer arc.deinit();
var fil: File = try Io.Dir.cwd().openFile(io, archive, .{}); //TODO: Handle error gracefully.
defer fil.close(io);
var arc: squashfs.Archive = try .initAdvanced(alloc, io, fil, offset, 0); //TODO: Update when memory size matters. //TODO: Handle error gracefully.
defer arc.deinit(io);
const options: squashfs.ExtractionOptions = .{
.threads = if (threads == 0) try std.Thread.getCpuCount() else threads,
.verbose = verbose,
.verbose_writer = if (verbose) &out.interface else null,
.ignore_xattr = ignore_xattrs,
.ignore_permissions = ignore_permissions,
};
if (force)
try std.fs.cwd().deleteTree(extLoc);
try arc.extract(alloc, extLoc, options); //TODO: Handle error gracefully.
try Io.Dir.cwd().deleteTree(io, extLoc);
try arc.extract(alloc, io, extLoc, options); //TODO: Handle error gracefully.
}
fn handleArgs(alloc: std.mem.Allocator, out: *Writer) !void {
var args = try std.process.argsWithAllocator(alloc);
defer args.deinit();
_ = args.next(); // args[0] is the application launch command.
while (args.next()) |arg| {
fn handleArgs(args: std.process.Args, out: *Writer) !void {
var arg_iter = args.iterate();
defer arg_iter.deinit();
_ = arg_iter.skip(); // args[0] is the application launch command.
while (arg_iter.next()) |arg| {
if (std.mem.eql(u8, arg, "-o")) {
const nxt = args.next();
const nxt = arg_iter.next();
if (nxt == null or nxt.?.len == 0) {
try out.print("-o must be followed by a number\n", .{});
return errors.InvalidArguments;
@@ -82,7 +85,7 @@ fn handleArgs(alloc: std.mem.Allocator, out: *Writer) !void {
};
continue;
} else if (std.mem.eql(u8, arg, "-d")) {
const nxt = args.next();
const nxt = arg_iter.next();
if (nxt == null or nxt.?.len == 0) {
try out.print("-d must be followed by a location\n", .{});
return errors.InvalidArguments;
@@ -90,7 +93,7 @@ fn handleArgs(alloc: std.mem.Allocator, out: *Writer) !void {
extLoc = nxt.?;
continue;
} else if (std.mem.eql(u8, arg, "-p")) {
const nxt = args.next();
const nxt = arg_iter.next();
if (nxt == null or nxt.?.len == 0) {
try out.print("-p must be followed by a number\n", .{});
return errors.InvalidArguments;
+4 -4
View File
@@ -5,17 +5,17 @@ const c = @import("c");
const Error = @import("decomp.zig").Error;
pub fn zlibDecompress(_: std.mem.Allocator, in: []u8, out: []u8) Error!usize {
var strem: c.zng_stream = .{
var strem: c.z_stream = .{
.next_in = in.ptr,
.avail_in = @truncate(in.len),
.next_out = out.ptr,
.avail_out = @truncate(out.len),
};
var res = c.zng_inflateInit(&strem);
var res = c.inflateInit(&strem);
if (res != c.Z_OK) return Error.ReadFailed;
defer _ = c.zng_inflateEnd(&strem);
defer _ = c.inflateEnd(&strem);
res = c.zng_inflate(&strem, c.Z_FULL_FLUSH);
res = c.inflate(&strem, c.Z_FULL_FLUSH);
if (res != c.Z_OK) return Error.ReadFailed;
return strem.total_out;
+4 -4
View File
@@ -19,7 +19,7 @@ cond: std.Io.Condition = .init,
max_mem: u64,
cur_mem: u64 = 0,
pub fn init(alloc: std.mem.Allocator, map: MemoryMap, compression: Decomp.Enum, max_mem: u64) DecompCache {
pub fn init(alloc: std.mem.Allocator, map: MemoryMap, compression: Decomp.Enum, max_mem: u64) !DecompCache {
return .{
.alloc = alloc,
.map = map,
@@ -55,8 +55,8 @@ pub fn get(self: *DecompCache, io: Io, offset: u64, compressed_size: u32, max_si
const cache = try self.cache.getOrPut(offset);
if (cache.found_existing) {
_ = cache.?.usage.fetchAdd(1, .acquire);
return cache.?.data;
_ = cache.value_ptr.usage.fetchAdd(1, .acquire);
return cache.value_ptr.data;
}
errdefer self.cache.removeByPtr(cache.key_ptr);
@@ -104,7 +104,7 @@ fn ensureSpace(self: *DecompCache, io: Io, size: u64) !void {
}
}
if (self.cur_mem + size <= self.max_mem) return;
try self.cond.wait(io, self.mut.mutex);
try self.cond.wait(io, &self.mut.mutex);
}
}
+1 -1
View File
@@ -46,7 +46,7 @@ pub fn init(alloc: std.mem.Allocator, rdr: *Reader, size: u32) !Directory {
}
}
return entries.toOwnedSlice(alloc);
return .{ .entries = try entries.toOwnedSlice(alloc) };
}
pub fn deinit(self: Directory, alloc: std.mem.Allocator) void {
for (self.entries) |entry|
+51
View File
@@ -0,0 +1,51 @@
const std = @import("std");
const Io = std.Io;
const DecompCache = @import("decomp_cache.zig");
const ExtractionOptions = @import("options.zig");
const Inode = @import("inode.zig");
const Superblock = @import("archive.zig").Superblock;
pub fn extract(alloc: std.mem.Allocator, io: Io, inode: Inode, cache: *DecompCache, super: Superblock, ext_loc: []const u8, options: ExtractionOptions) !void {
_ = alloc;
_ = io;
_ = inode;
_ = cache;
_ = super;
_ = ext_loc;
_ = options;
return error.TODO;
}
pub fn extractDir(alloc: std.mem.Allocator, io: Io, path: []const u8, d: anytype) Error!PathReturn {}
pub fn extractFile(alloc: std.mem.Allocator, io: Io, path: []const u8, d: anytype) Error!PathReturn {
const atomic = try Io.Dir.cwd().createFileAtomic(io, path, .{});
defer atomic.deinit(io);
// TODO
try atomic.link(io);
// return .{
// .path = path,
// };
return error.TODO;
}
// Utility types
const ReturnUnion = union {
path_ret: Error!PathReturn,
};
const Error = error{};
const PathReturn = struct {
path: []const u8,
uid_idx: u32,
gid_idx: u32,
mod_time: u32,
permission: u16,
xattr_idx: ?u32,
};
+19 -9
View File
@@ -1,3 +1,11 @@
const std = @import("std");
const Io = std.Io;
const Archive = @import("archive.zig");
const Directory = @import("directory.zig");
const ExtractionOptions = @import("options.zig");
const Inode = @import("inode.zig");
const SfsFile = @This();
alloc: std.mem.Allocator,
@@ -28,7 +36,7 @@ pub fn initDirEntry(alloc: std.mem.Allocator, io: Io, archive: *Archive, entry:
.inode = try .initDirEntry(
alloc,
io,
archive.cache,
&archive.cache,
archive.super.inode_start,
archive.super.block_size,
entry,
@@ -61,7 +69,7 @@ pub fn open(self: SfsFile, alloc: std.mem.Allocator, io: Io, filepath: []const u
const first_element: []const u8 = std.mem.sliceTo(path, '/');
const dir: Directory = try self.inode.directory(alloc, io, self.archive.cache, self.archive.super.dir_start);
const dir: Directory = try self.inode.directory(alloc, io, &self.archive.cache, self.archive.super.dir_start);
defer dir.deinit(alloc);
var cur_slice = dir.entries;
@@ -76,7 +84,7 @@ pub fn open(self: SfsFile, alloc: std.mem.Allocator, io: Io, filepath: []const u
} else {
return error.NotFound;
}
if (first_element.len == path) return .initDirEntry(alloc, io, self.archive, cur_slice[idx]);
if (first_element.len == path.len) return .initDirEntry(alloc, io, self.archive, cur_slice[idx]);
if (cur_slice[idx].type != .dir) return error.NotFound;
const tmp_file: SfsFile = try .initDirEntry(alloc, io, self.archive, cur_slice[idx]);
defer tmp_file.deinit();
@@ -84,9 +92,11 @@ pub fn open(self: SfsFile, alloc: std.mem.Allocator, io: Io, filepath: []const u
return tmp_file.open(alloc, io, path[first_element.len..]);
}
const std = @import("std");
const Io = std.Io;
const Archive = @import("archive.zig");
const Directory = @import("directory.zig");
const Inode = @import("inode.zig");
pub fn extract(self: SfsFile, alloc: std.mem.Allocator, io: Io, ext_dir: []const u8, options: ExtractionOptions) !void {
_ = self;
_ = alloc;
_ = io;
_ = ext_dir;
_ = options;
return error.TODO;
}
+2 -2
View File
@@ -280,7 +280,7 @@ const ExtFile = struct {
.blocks = blocks,
};
}
pub fn deinit(self: File, alloc: std.mem.Allocator) void {
pub fn deinit(self: ExtFile, alloc: std.mem.Allocator) void {
alloc.free(self.blocks);
}
};
@@ -342,7 +342,7 @@ const ExtSymlink = struct {
};
}
pub fn deinit(self: Symlink, alloc: std.mem.Allocator) void {
pub fn deinit(self: ExtSymlink, alloc: std.mem.Allocator) void {
alloc.free(self.target);
}
};