diff --git a/README.md b/README.md index 2eeb5a0..2fd8dd1 100644 --- a/README.md +++ b/README.md @@ -26,3 +26,25 @@ Most features are present except for the following: * xattrs are not applied on extraction * Only zstd c library is implemented (all others result in error.TODO). * When using Zig decompression libraries then lzo and lz4 compression types are unavailable. I don't _really_ plan on spending the time to find and validate a library since neither is popular. + +## Building considerations + +Compilation without `use_c_libs` works completely fine, but Zig has issues with some symbols from the lzo library that needs to be manually fixed. In particular you need to fix the definitions for `lzo_bytep` and `lzo_voidp` to be `*u8` and `?*anyopaque` respectively. + +```zig +pub const lzo_bytep = @compileError("unable to translate C expr: unexpected token ''"); +// /usr/include/lzo/lzoconf.h:148:9 +pub const lzo_charp = @compileError("unable to translate C expr: unexpected token ''"); +// /usr/include/lzo/lzoconf.h:149:9 +pub const lzo_voidp = @compileError("unable to translate C expr: unexpected token ''"); +``` + +to + +```zig +pub const lzo_bytep = *u8; +// /usr/include/lzo/lzoconf.h:148:9 +pub const lzo_charp = @compileError("unable to translate C expr: unexpected token ''"); +// /usr/include/lzo/lzoconf.h:149:9 +pub const lzo_voidp = ?*anyopaque; +``` diff --git a/build.zig b/build.zig index 427763d..1bf30bb 100644 --- a/build.zig +++ b/build.zig @@ -16,8 +16,13 @@ pub fn build(b: *std.Build) !void { .link_libc = if (use_c_libs_option == true) true else false, }); mod.addOptions("config", zig_squashfs_options); - if (use_c_libs_option == true) + if (use_c_libs_option == true) { + mod.linkSystemLibrary("zlib", .{}); + mod.linkSystemLibrary("lzma", .{}); + mod.linkSystemLibrary("minilzo", .{}); + mod.linkSystemLibrary("lz4", .{}); mod.linkSystemLibrary("zstd", .{}); + } const unsquashfs_options = b.addOptions(); unsquashfs_options.addOption(std.SemanticVersion, "version", try std.SemanticVersion.parse(version_string_option orelse "0.0.0-testing")); diff --git a/src/archive.zig b/src/archive.zig index ec41ca2..859912a 100644 --- a/src/archive.zig +++ b/src/archive.zig @@ -3,6 +3,7 @@ const std = @import("std"); const File = std.fs.File; +const builtin = @import("builtin"); const Decomp = @import("decomp.zig"); const ExtractionOptions = @import("options.zig"); @@ -15,6 +16,8 @@ const Table = @import("table.zig").Table; const MetadataReader = @import("util/metadata.zig"); const OffsetFile = @import("util/offset_file.zig"); +const config = if (builtin.is_test) .{ .use_c_libs = true } else @import("config"); + /// Information about a fragment section. Multiple fragments are contained in the block described by a single FragEntry. /// The offset into the block and fragment size is stored in the file's inode. pub const FragEntry = packed struct { @@ -77,7 +80,8 @@ pub fn initAdvanced(alloc: std.mem.Allocator, fil: File, offset: u64, threads: u .lzma => Decomp.lzmaDecompress, .xz => Decomp.xzDecompress, .zstd => Decomp.zstdDecompress, - else => return error.UnsupportedCompressionType, + .lz4 => if (config.use_c_libs) Decomp.cLz4 else return error.Lz4Unsupported, + .lzo => if (config.use_c_libs) Decomp.cLzo else return error.LzoUnsupported, }, .super = super, diff --git a/src/decomp.zig b/src/decomp.zig index 7357a85..63cd749 100644 --- a/src/decomp.zig +++ b/src/decomp.zig @@ -8,7 +8,13 @@ const builtin = @import("builtin"); const config = if (builtin.is_test) .{ .use_c_libs = true } else @import("config"); const c = @cImport({ - @cInclude("zstd.h"); + if (config.use_c_libs) { + @cInclude("zlib.h"); + @cInclude("lzma.h"); + @cInclude("lzo/minilzo.h"); + @cInclude("lz4.h"); + @cInclude("zstd.h"); + } }); pub const CompressionType = enum(u16) { @@ -33,9 +39,15 @@ fn zigGzip(alloc: std.mem.Allocator, in: []u8, out: []u8) anyerror!usize { } fn cGzip(alloc: std.mem.Allocator, in: []u8, out: []u8) anyerror!usize { _ = alloc; - _ = in; - _ = out; - return error.TODO; + var out_len: usize = out.len; + const res = c.uncompress(out.ptr, &out_len, in.ptr, in.len); + return switch (res) { + c.Z_OK => out_len, + c.Z_MEM_ERROR => error.NotEnoughMemory, + c.Z_BUF_ERROR => error.OutBufferTooSmall, + c.Z_DATA_ERROR => error.BadData, + else => error.UnknownResult, + }; } pub const lzmaDecompress = if (config.use_c_libs) cLzma else zigLzma; @@ -47,9 +59,65 @@ fn zigLzma(alloc: std.mem.Allocator, in: []u8, out: []u8) anyerror!usize { } fn cLzma(alloc: std.mem.Allocator, in: []u8, out: []u8) anyerror!usize { _ = alloc; - _ = in; - _ = out; - return error.TODO; + var stream: c.lzma_stream = .{ + .next_in = in.ptr, + .avail_in = in.len, + .next_out = out.ptr, + .avail_out = out.len, + }; + var res = c.lzma_alone_decoder(&stream, in.len * 2); + switch (res) { + c.LZMA_OK => {}, + c.LZMA_MEM_ERROR => return error.LzmaMemoryError, + c.LZMA_PROG_ERROR => return error.LzmaProgramError, + else => return error.UnknownResult, + } + defer c.lzma_end(&stream); + while (res == c.LZMA_OK) + res = c.lzma_code(&stream, c.LZMA_RUN); + return switch (res) { + c.LZMA_STREAM_END => stream.total_out, + c.LZMA_MEM_ERROR => error.LzmaMemoryError, + c.LZMA_MEMLIMIT_ERROR => error.LzmaMemoryLimit, + c.LZMA_FORMAT_ERROR => error.LzmaBadFormat, + c.LZMA_DATA_ERROR => error.LzmaDataCorrupt, + c.LZMA_BUF_ERROR => error.LzmaCannotProgress, + c.LZMA_PROG_ERROR => error.LzmaProgramError, + else => error.UnknownResult, + }; +} + +// pub const lzoDecompress = if (config.use_c_libs) cLzo else zigLzo; + +// fn zigLzo(alloc: std.mem.Allocator, in: []u8, out: []u8) anyerror!usize { +// _ = alloc; +// _ = in; +// _ = out; +// return error.LzoUnsupported; +// } +pub fn cLzo(alloc: std.mem.Allocator, in: []u8, out: []u8) anyerror!usize { + _ = alloc; + var res = c.lzo_init(); + if (res != 0) return error.LzoInitFailed; + var out_len: usize = out.len; + res = c.lzo1x_decompress(in.ptr, in.len, out.ptr, &out_len, null); + return switch (res) { + c.LZO_E_OK => out_len, + c.LZO_E_ERROR => error.LzoError, + c.LZO_E_OUT_OF_MEMORY => error.LzoOutOfMemory, + c.LZO_E_NOT_COMPRESSIBLE => error.LzoNotCompressible, + c.LZO_E_INPUT_OVERRUN => error.LzoInputOverrun, + c.LZO_E_OUTPUT_OVERRUN => error.LzoOutputOverrun, + c.LZO_E_LOOKBEHIND_OVERRUN => error.LzoLookbehindOverrun, + c.LZO_E_EOF_NOT_FOUND => error.LzoEofNotFound, + c.LZO_E_INPUT_NOT_CONSUMED => error.LzoInputNotConsumed, + c.LZO_E_NOT_YET_IMPLEMENTED => error.LzoNotYetImplemented, + c.LZO_E_INVALID_ARGUMENT => error.LzoInvalidArgument, + c.LZO_E_INVALID_ALIGNMENT => error.LzoInvalidAlignment, + c.LZO_E_OUTPUT_NOT_CONSUMED => error.LzoOutputNotConsumed, + c.LZO_E_INTERNAL_ERROR => error.LzoInternalError, + else => error.UnknownResult, + }; } pub const xzDecompress = if (config.use_c_libs) cXz else zigXz; @@ -61,9 +129,47 @@ fn zigXz(alloc: std.mem.Allocator, in: []u8, out: []u8) anyerror!usize { } fn cXz(alloc: std.mem.Allocator, in: []u8, out: []u8) anyerror!usize { _ = alloc; - _ = in; - _ = out; - return error.TODO; + var stream: c.lzma_stream = .{ + .next_in = in.ptr, + .avail_in = in.len, + .next_out = out.ptr, + .avail_out = out.len, + }; + var res = c.lzma_stream_decoder(&stream, in.len * 2, 0); + switch (res) { + c.LZMA_OK => {}, + c.LZMA_MEM_ERROR => return error.LzmaMemoryError, + c.LZMA_PROG_ERROR => return error.LzmaProgramError, + else => return error.UnknownResult, + } + defer c.lzma_end(&stream); + while (res == c.LZMA_OK) + res = c.lzma_code(&stream, c.LZMA_RUN); + return switch (res) { + c.LZMA_STREAM_END => stream.total_out, + c.LZMA_MEM_ERROR => error.LzmaMemoryError, + c.LZMA_MEMLIMIT_ERROR => error.LzmaMemoryLimit, + c.LZMA_FORMAT_ERROR => error.LzmaBadFormat, + c.LZMA_DATA_ERROR => error.LzmaDataCorrupt, + c.LZMA_BUF_ERROR => error.LzmaCannotProgress, + c.LZMA_PROG_ERROR => error.LzmaProgramError, + else => error.UnknownResult, + }; +} + +// pub const lz4Decompress = if (config.use_c_libs) cLz4 else zigLz4; + +// fn zigLz4(alloc: std.mem.Allocator, in: []u8, out: []u8) anyerror!usize { +// _ = alloc; +// _ = in; +// _ = out; +// return error.Lz4Unsupported; +// } +pub fn cLz4(alloc: std.mem.Allocator, in: []u8, out: []u8) anyerror!usize { + _ = alloc; + const res = c.LZ4_decompress_safe(in.ptr, out.ptr, @intCast(in.len), @intCast(out.len)); + if (res > 0) return @abs(res); // TODO: Find out what error values it can return. + return error.Lz4DecompressFailed; } pub const zstdDecompress = if (config.use_c_libs) cZstd else zigZstd;