From d0787a5200476b62c8c116c503a4227151b56b91 Mon Sep 17 00:00:00 2001 From: "Caleb J. Gardner" Date: Fri, 3 Apr 2026 22:27:40 -0500 Subject: [PATCH] Fixed a couple decompressor issues. Re-added ExtractionOptions --- src/archive.zig | 26 ++++++++++++++++++-------- src/bin/unsquashfs.zig | 23 +++++++++++------------ src/decomp.zig | 7 ++----- src/decomp/c/lz4.zig | 4 ++-- src/decomp/c/lzma.zig | 24 ++++++++++++------------ src/decomp/c/lzo.zig | 2 +- src/decomp/c/xz.zig | 28 ++++++++++++++-------------- src/decomp/c/zstd.zig | 4 ++-- src/decomp/zig/lzma.zig | 2 +- src/decomp/zig/xz.zig | 2 +- src/decomp/zig/zlib.zig | 2 +- src/decomp/zig/zstd.zig | 2 +- src/options.zig | 33 +++++++++++++++++++++++++++++++++ src/root.zig | 2 ++ 14 files changed, 101 insertions(+), 60 deletions(-) create mode 100644 src/options.zig diff --git a/src/archive.zig b/src/archive.zig index dbaafe7..87a4572 100644 --- a/src/archive.zig +++ b/src/archive.zig @@ -3,13 +3,22 @@ const std = @import("std"); const DecompTypes = @import("decomp/types.zig"); const Decompressor = @import("decomp.zig"); const Inode = @import("inode.zig"); +const ExtractionOptions = @import("options.zig"); + +pub const Error = error{ + BadMagic, + BadBlockLog, + BadVersion, + BadCheck, +}; const Archive = @This(); super: Superblock, -stateless_decomp: Decompressor.StatelessDecomp, +stateless_decomp: Decompressor, +/// Create an Archive from a File. pub fn init(fil: std.fs.File, offset: u64) !Archive { var super: Superblock = undefined; var fil_rdr = fil.reader(&[0]u8{}); @@ -20,16 +29,17 @@ pub fn init(fil: std.fs.File, offset: u64) !Archive { return .{ .super = super, - .stateless_decomp = DecompTypes.getStatelessFn(super.compression), + .stateless_decomp = .{ .vtable = &.{ .stateless = try DecompTypes.getStatelessFn(super.compression) } }, }; } -pub const Error = error{ - BadMagic, - BadBlockLog, - BadVersion, - BadCheck, -}; +pub fn extract(self: Archive, alloc: std.mem.Allocator, path: []const u8, options: ExtractionOptions) !void{ + _ = self; + _ = alloc; + _ = path; + _ = options; + return error.TODO; +} // Superblock diff --git a/src/bin/unsquashfs.zig b/src/bin/unsquashfs.zig index 7811f75..0d85644 100644 --- a/src/bin/unsquashfs.zig +++ b/src/bin/unsquashfs.zig @@ -51,18 +51,17 @@ pub fn main() !void { } var fil: std.fs.File = try std.fs.cwd().openFile(archive, .{}); //TODO: Handle error gracefully. defer fil.close(); - // var arc: squashfs.Archive = try .init(fil, offset); //TODO: Update when memory size matters. //TODO: Handle error gracefully. - // defer arc.deinit(); - // 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. + var arc: squashfs.Archive = try .init(fil, offset); //TODO: Handle error gracefully. + 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. } fn handleArgs(alloc: std.mem.Allocator, out: *Writer) !void { diff --git a/src/decomp.zig b/src/decomp.zig index 7e11063..29c994a 100644 --- a/src/decomp.zig +++ b/src/decomp.zig @@ -11,8 +11,8 @@ pub const Error = error{ const Decompressor = @This(); -alloc: std.mem.Allocator = std.heap.page_allocator, -vtable: *struct { +alloc: std.mem.Allocator = std.heap.smp_allocator, +vtable: *const struct { decompress: *const fn (*const Decompressor, in: []u8, out: []u8) Error!usize = defaultDecompress, stateless: StatelessDecomp, }, @@ -20,9 +20,6 @@ vtable: *struct { pub fn decompress(self: *const Decompressor, in: []u8, out: []u8) Error!usize { return self.vtable.decompress(self, in, out); } -pub fn stateless(self: Decompressor, alloc: std.mem.Allocator, in: []u8, out: []u8) Error!usize { - return self.vtable.stateless(alloc, in, out); -} fn defaultDecompress(self: *const Decompressor, in: []u8, out: []u8) Error!usize { return self.vtable.stateless(self.alloc, in, out); diff --git a/src/decomp/c/lz4.zig b/src/decomp/c/lz4.zig index 3a5a2cb..928378f 100644 --- a/src/decomp/c/lz4.zig +++ b/src/decomp/c/lz4.zig @@ -3,10 +3,10 @@ const std = @import("std"); const c = @import("../../c_libs.zig").c; const Decompressor = @import("../../decomp.zig"); -interface: Decompressor = .{ .vtable = .{ .stateless = stateless } }, +interface: Decompressor = .{ .vtable = &.{ .stateless = stateless } }, pub fn stateless(_: std.mem.Allocator, in: []u8, out: []u8) Decompressor.Error!usize { - const res = c.LZ4_decompress_safe(in.ptr, out.ptr, in.len, out.len); + const res = c.LZ4_decompress_safe(in.ptr, out.ptr, @intCast(in.len), @intCast(out.len)); if (res > 0) return @abs(res); return Decompressor.Error.ReadFailed; // TOOD: Find out what errors can be returned. } diff --git a/src/decomp/c/lzma.zig b/src/decomp/c/lzma.zig index a95606f..b06afd1 100644 --- a/src/decomp/c/lzma.zig +++ b/src/decomp/c/lzma.zig @@ -49,10 +49,10 @@ fn decompress(decomp: *const Decompressor, in: []u8, out: []u8) Decompressor.Err stream.avail_in = in.len; stream.next_out = out.ptr; stream.avail_out = out.len; - var res = c.lzma_alone_decoder(stream, out.len, 0); + var res = c.lzma_alone_decoder(stream, out.len); decodeResult(res) catch |err| { self.err = err; - lzmaErrorToDecompError(err); + return lzmaErrorToDecompError(err); }; while (true) { res = c.lzma_code(&stream, c.LZMA_RUN); @@ -60,7 +60,7 @@ fn decompress(decomp: *const Decompressor, in: []u8, out: []u8) Decompressor.Err if (res == c.LZMA_STREAM_END) break; decodeResult(res) catch |err| { self.err = err; - lzmaErrorToDecompError(err); + return lzmaErrorToDecompError(err); }; } return stream.total_out; @@ -71,19 +71,19 @@ pub fn stateless(alloc: std.mem.Allocator, in: []u8, out: []u8) Decompressor.Err .avail_in = in.len, .next_out = out.ptr, .avail_out = out.len, - .allocator = .{ + .allocator = &.{ .alloc = lzmaAlloc, .free = lzmaFree, - .@"opaque" = &alloc, + .@"opaque" = @ptrCast(@constCast(&alloc)), }, }; - var res = c.lzma_alone_decoder(&stream, out.len, 0); - decodeResult(res) catch |err| lzmaErrorToDecompError(err); + var res = c.lzma_alone_decoder(&stream, out.len); + decodeResult(res) catch |err| return lzmaErrorToDecompError(err); while (true) { res = c.lzma_code(&stream, c.LZMA_RUN); if (res == c.LZMA_OK) continue; if (res == c.LZMA_STREAM_END) break; - decodeResult(res) catch |err| lzmaErrorToDecompError(err); + decodeResult(res) catch |err| return lzmaErrorToDecompError(err); } return stream.total_out; } @@ -107,7 +107,7 @@ inline fn decodeResult(res: c_uint) Error!void { } fn lzmaErrorToDecompError(err: Error) Decompressor.Error { switch (err) { - Error.OutOfMemory => return err, + Error.OutOfMemory => return Decompressor.Error.OutOfMemory, Error.UnsupportedCheck => return Decompressor.Error.ReadFailed, Error.Format => return Decompressor.Error.ReadFailed, Error.Options => return Decompressor.Error.ReadFailed, @@ -120,12 +120,12 @@ fn lzmaErrorToDecompError(err: Error) Decompressor.Error { } fn lzmaAlloc(ptr: ?*anyopaque, _: usize, size: usize) callconv(.c) ?*anyopaque { - var alloc: *std.mem.Allocator = @ptrCast(ptr); - return alloc.rawAlloc(size, .@"1", 0) catch return null; + var alloc: *std.mem.Allocator = @alignCast(@ptrCast(ptr)); + return alloc.rawAlloc(size, .@"1", 0); } fn lzmaFree(ptr: ?*anyopaque, alloc_ptr: ?*anyopaque) callconv(.c) void { if (alloc_ptr == null) return; - var alloc: *std.mem.Allocator = @ptrCast(ptr); + var alloc: *std.mem.Allocator = @alignCast(@ptrCast(ptr)); alloc.rawFree(@ptrCast(alloc_ptr), .@"1", 0); } diff --git a/src/decomp/c/lzo.zig b/src/decomp/c/lzo.zig index 7ed04ad..f373e27 100644 --- a/src/decomp/c/lzo.zig +++ b/src/decomp/c/lzo.zig @@ -3,7 +3,7 @@ const std = @import("std"); const c = @import("../../c_libs.zig").c; const Decompressor = @import("../../decomp.zig"); -interface: Decompressor = .{ .vtable = .{ .stateless = stateless } }, +interface: Decompressor = .{ .vtable = &.{ .stateless = stateless } }, pub fn stateless(_: std.mem.Allocator, in: []u8, out: []u8) Decompressor.Error!usize { var out_len = out.len; diff --git a/src/decomp/c/xz.zig b/src/decomp/c/xz.zig index b324440..9d8cb52 100644 --- a/src/decomp/c/xz.zig +++ b/src/decomp/c/xz.zig @@ -23,12 +23,12 @@ pub fn init(alloc: std.mem.Allocator) !Xz { pub fn deinit(self: *Xz) void { var values = self.streams.valueIterator(); while (values.next()) |val| { - c.lzma_end(val); + c.xz_end(val); } self.streams.deinit(); } -fn getOrCreate(self: *Xz) !*c.lzma_stream { +fn getOrCreate(self: *Xz) !*c.xz_stream { const res = try self.streams.getOrPut(std.Thread.getCurrentId()); if (res.found_existing) return res.value_ptr; res.value_ptr.* = .{ @@ -49,10 +49,10 @@ fn decompress(decomp: *const Decompressor, in: []u8, out: []u8) Decompressor.Err stream.avail_in = in.len; stream.next_out = out.ptr; stream.avail_out = out.len; - var res = c.lzma_stream_decoder(stream, out.len, 0); + var res = c.lzma_alone_decoder(stream, out.len); decodeResult(res) catch |err| { self.err = err; - xzErrorToDecompError(err); + return xzErrorToDecompError(err); }; while (true) { res = c.lzma_code(&stream, c.LZMA_RUN); @@ -60,7 +60,7 @@ fn decompress(decomp: *const Decompressor, in: []u8, out: []u8) Decompressor.Err if (res == c.LZMA_STREAM_END) break; decodeResult(res) catch |err| { self.err = err; - xzErrorToDecompError(err); + return xzErrorToDecompError(err); }; } return stream.total_out; @@ -71,19 +71,19 @@ pub fn stateless(alloc: std.mem.Allocator, in: []u8, out: []u8) Decompressor.Err .avail_in = in.len, .next_out = out.ptr, .avail_out = out.len, - .allocator = .{ + .allocator = &.{ .alloc = lzmaAlloc, .free = lzmaFree, - .@"opaque" = &alloc, + .@"opaque" = @ptrCast(@constCast(&alloc)), }, }; - var res = c.lzma_stream_decoder(&stream, out.len, 0); - decodeResult(res) catch |err| xzErrorToDecompError(err); + var res = c.lzma_alone_decoder(&stream, out.len); + decodeResult(res) catch |err| return xzErrorToDecompError(err); while (true) { res = c.lzma_code(&stream, c.LZMA_RUN); if (res == c.LZMA_OK) continue; if (res == c.LZMA_STREAM_END) break; - decodeResult(res) catch |err| xzErrorToDecompError(err); + decodeResult(res) catch |err| return xzErrorToDecompError(err); } return stream.total_out; } @@ -107,7 +107,7 @@ inline fn decodeResult(res: c_uint) Error!void { } fn xzErrorToDecompError(err: Error) Decompressor.Error { switch (err) { - Error.OutOfMemory => return err, + Error.OutOfMemory => return Decompressor.Error.OutOfMemory, Error.UnsupportedCheck => return Decompressor.Error.ReadFailed, Error.Format => return Decompressor.Error.ReadFailed, Error.Options => return Decompressor.Error.ReadFailed, @@ -120,12 +120,12 @@ fn xzErrorToDecompError(err: Error) Decompressor.Error { } fn lzmaAlloc(ptr: ?*anyopaque, _: usize, size: usize) callconv(.c) ?*anyopaque { - var alloc: *std.mem.Allocator = @ptrCast(ptr); - return alloc.rawAlloc(size, .@"1", 0) catch return null; + var alloc: *std.mem.Allocator = @alignCast(@ptrCast(ptr)); + return alloc.rawAlloc(size, .@"1", 0); } fn lzmaFree(ptr: ?*anyopaque, alloc_ptr: ?*anyopaque) callconv(.c) void { if (alloc_ptr == null) return; - var alloc: *std.mem.Allocator = @ptrCast(ptr); + var alloc: *std.mem.Allocator = @alignCast(@ptrCast(ptr)); alloc.rawFree(@ptrCast(alloc_ptr), .@"1", 0); } diff --git a/src/decomp/c/zstd.zig b/src/decomp/c/zstd.zig index 847a9c6..9556183 100644 --- a/src/decomp/c/zstd.zig +++ b/src/decomp/c/zstd.zig @@ -57,7 +57,6 @@ pub fn stateless(alloc: std.mem.Allocator, in: []u8, out: []u8) Decompressor.Err inline fn decodeError(res: usize) Error!void { if (c.ZSTD_isError(res) == 0) return; return switch (c.ZSTD_getErrorCode(res)) { - c.ZSTD_error_GENERIC => Error.Generic, c.ZSTD_error_prefix_unknown => Error.PrefixUnknown, c.ZSTD_error_version_unsupported => Error.VersionUnsupported, c.ZSTD_error_frameParameter_unsupported => Error.FrameParameterUnsupported, @@ -91,11 +90,12 @@ inline fn decodeError(res: usize) Error!void { c.ZSTD_error_srcBuffer_wrong => Error.SrcBufferWrong, c.ZSTD_error_sequenceProducer_failed => Error.SequenceProducerFailed, c.ZSTD_error_externalSequences_invalid => Error.ExternalSequencesInvalid, + else => Error.Generic, }; } inline fn ZstdErrorToDecompError(err: Error) Decompressor.Error { return switch (err) { - Error.OutOfMemory => err, + Error.OutOfMemory => Decompressor.Error.OutOfMemory, Error.Generic => Decompressor.Error.ReadFailed, Error.PrefixUnknown => Decompressor.Error.ReadFailed, Error.VersionUnsupported => Decompressor.Error.ReadFailed, diff --git a/src/decomp/zig/lzma.zig b/src/decomp/zig/lzma.zig index 1f5c089..4f0a128 100644 --- a/src/decomp/zig/lzma.zig +++ b/src/decomp/zig/lzma.zig @@ -4,7 +4,7 @@ const Reader = std.Io.Reader; const Decompressor = @import("../../decomp.zig"); -interface: Decompressor = .{ .vtable = .{ .stateless = stateless } }, +interface: Decompressor = .{ .vtable = &.{ .stateless = stateless } }, pub fn stateless(alloc: std.mem.Allocator, in: []u8, out: []u8) Decompressor.Error!usize { var rdr: Reader = .static(in); diff --git a/src/decomp/zig/xz.zig b/src/decomp/zig/xz.zig index d53b368..72db802 100644 --- a/src/decomp/zig/xz.zig +++ b/src/decomp/zig/xz.zig @@ -4,7 +4,7 @@ const Reader = std.Io.Reader; const Decompressor = @import("../../decomp.zig"); -interface: Decompressor = .{ .vtable = .{ .stateless = stateless } }, +interface: Decompressor = .{ .vtable = &.{ .stateless = stateless } }, pub fn stateless(alloc: std.mem.Allocator, in: []u8, out: []u8) Decompressor.Error!usize { var rdr: Reader = .static(in); diff --git a/src/decomp/zig/zlib.zig b/src/decomp/zig/zlib.zig index 997efd2..450a777 100644 --- a/src/decomp/zig/zlib.zig +++ b/src/decomp/zig/zlib.zig @@ -4,7 +4,7 @@ const Reader = std.Io.Reader; const Decompressor = @import("../../decomp.zig"); -interface: Decompressor = .{ .vtable = .{ .stateless = stateless } }, +interface: Decompressor = .{ .vtable = &.{ .stateless = stateless } }, pub fn stateless(alloc: std.mem.Allocator, in: []u8, out: []u8) Decompressor.Error!usize { const buf = try alloc.alloc(u8, out.len); diff --git a/src/decomp/zig/zstd.zig b/src/decomp/zig/zstd.zig index 479a391..4aec25f 100644 --- a/src/decomp/zig/zstd.zig +++ b/src/decomp/zig/zstd.zig @@ -4,7 +4,7 @@ const Reader = std.Io.Reader; const Decompressor = @import("../../decomp.zig"); -interface: Decompressor = .{ .vtable = .{ .stateless = stateless } }, +interface: Decompressor = .{ .vtable = &.{ .stateless = stateless } }, pub fn stateless(alloc: std.mem.Allocator, in: []u8, out: []u8) Decompressor.Error!usize { const buf = try alloc.alloc(u8, out.len * 2); diff --git a/src/options.zig b/src/options.zig new file mode 100644 index 0000000..c139c62 --- /dev/null +++ b/src/options.zig @@ -0,0 +1,33 @@ +//! Options for file/directory extraction. + +const std = @import("std"); +const Writer = std.Io.Writer; + +const ExtractionOptions = @This(); + +/// The number of threads used for extraction. 0 implies single threaded. +threads: usize = 1, +/// Don't set the file's owner & permissions after extraction +ignore_permissions: bool = false, +/// Don't set xattr values. Currently xattrs are never set anyway. +ignore_xattr: bool = false, +/// Replace symlinks with their target. +dereference_symlinks: bool = false, +/// Verbose logging. If true, verbose_writer must be set +verbose: bool = false, +/// Where to print verbose log. +verbose_writer: ?*Writer = null, + +pub const SingleThreadedDefault: ExtractionOptions = .{}; +pub fn Default() !ExtractionOptions { + return .{ + .threads = try std.Thread.getCpuCount(), + }; +} +pub fn VerboseDefault(wrt: *Writer) !ExtractionOptions { + return .{ + .verbose = true, + .verbose_writer = wrt, + .threads = try std.Thread.getCpuCount(), + }; +} diff --git a/src/root.zig b/src/root.zig index 78c33c4..25ea3b2 100644 --- a/src/root.zig +++ b/src/root.zig @@ -1 +1,3 @@ pub const Archive = @import("archive.zig"); + +pub const ExtractionOptions = @import("options.zig"); \ No newline at end of file