Compare commits
3 Commits
3ea3d8e9a0
...
a9e50a0ff5
| Author | SHA1 | Date | |
|---|---|---|---|
| a9e50a0ff5 | |||
| 712c4d0a19 | |||
| 5975bbb4a2 |
@@ -8,13 +8,15 @@ A library and application to decompress or view squashfs archives.
|
|||||||
|
|
||||||
Overall works, but currently is missing some features ([see below](#capabilities)) and has significantly slow performance compared to `unsquashfs` ([see below](#performance)).
|
Overall works, but currently is missing some features ([see below](#capabilities)) and has significantly slow performance compared to `unsquashfs` ([see below](#performance)).
|
||||||
|
|
||||||
Currently things are still in flux after Zig 0.16's Io changes and the documentation below *might* not be up to date.
|
|
||||||
|
|
||||||
## Build options
|
## Build options
|
||||||
|
|
||||||
> `-Duse_c_libs=true`
|
> `-Duse_zig_decomp=true`
|
||||||
|
|
||||||
Instead of using Zig's standard library for decompression, use the system's C libraries. Has the benefit of being much faster and enabling LZO and LZ4 decompression.
|
Instead of using C libraries for decompression, use Zig's standard library for decompression. If using this option LZO and LZ4 decomrpession types are unsupported and decompression times will be significantly longer.
|
||||||
|
|
||||||
|
> `-Ddynamic=true`
|
||||||
|
|
||||||
|
Dynamicly link C libraries (if they're used) instead of statically linking them.
|
||||||
|
|
||||||
> `-Dallow_lzo=true`
|
> `-Dallow_lzo=true`
|
||||||
|
|
||||||
@@ -37,20 +39,20 @@ Most features are present except for the following:
|
|||||||
|
|
||||||
## Performance
|
## Performance
|
||||||
|
|
||||||
This is some basic observation's I've made about this library's performance when compared to `unsquashfs`. Unless otherwise stated, most observations were made when extracting my test archive (which is fairly small and uses zstd compression) and with `-Doptimize=ReleaseFast`.
|
This is some basic observation's I've made about this library's performance when compared to `unsquashfs`. Unless otherwise stated, most observations were made when extracting my test archive which is fairly small and uses zstd compression with `-Doptimize=ReleaseFast`.
|
||||||
|
|
||||||
Currently, my only performance checks are checking execution time, nothing deeper.
|
Currently, my only performance checks are checking execution time, nothing deeper.
|
||||||
|
|
||||||
* Currently, using my test archive, performance matches `unsquashfs`.
|
* Currently, using my test archive, performance aproximately matches `unsquashfs` when multi-threaded, but significantly slower when single-threaded.
|
||||||
* Using Zig decompression libraries *significantly* increases decompression time by 5x. Under ideal circumstances.
|
* Using Zig decompression libraries *significantly* increases decompression time by 5x. Under ideal circumstances.
|
||||||
* Performance improvements/regressions will be common. I'm still learning Zig.
|
* Performance improvements/regressions will be common. I'm still learning Zig.
|
||||||
|
|
||||||
Example Times:
|
Example Times:
|
||||||
|
|
||||||
* *unsquashfs, multi-threaded*: .12s
|
* *unsquashfs, multi-threaded*: .15s
|
||||||
* *unsquashfs, single-threaded*: .13s
|
* *unsquashfs, single-threaded*: .16s
|
||||||
* *C-libs, single-threaded*: CURRENTLY UNTESTED
|
* *C-libs, single-threaded*: .36s
|
||||||
* *C-libs, multi-threaded*: .16s
|
* *C-libs, multi-threaded*: .14s
|
||||||
* *Zig-libs, single-threaded*: CURRENTLY UNTESTED
|
* *Zig-libs, single-threaded*: CURRENTLY UNTESTED
|
||||||
* *Zig-libs, multi-threaded*: .76s
|
* *Zig-libs, multi-threaded*: .76s
|
||||||
|
|
||||||
|
|||||||
@@ -5,4 +5,13 @@ ARCHIVE="testing/LinuxPATest.sfs"
|
|||||||
REF_EXT_LOC="testing/LinuxPAReference"
|
REF_EXT_LOC="testing/LinuxPAReference"
|
||||||
PROG_EXT_LOC="testing/LinuxPABinTest"
|
PROG_EXT_LOC="testing/LinuxPABinTest"
|
||||||
|
|
||||||
|
echo "Testing Multi-threaded Performance"
|
||||||
|
echo ""
|
||||||
|
|
||||||
hyperfine --warmup 5 --prepare "rm -rf $REF_EXT_LOC && rm -rf $PROG_EXT_LOC" "unsquashfs -d $REF_EXT_LOC $ARCHIVE" "zig-out/bin/unsquashfs -d $PROG_EXT_LOC $ARCHIVE"
|
hyperfine --warmup 5 --prepare "rm -rf $REF_EXT_LOC && rm -rf $PROG_EXT_LOC" "unsquashfs -d $REF_EXT_LOC $ARCHIVE" "zig-out/bin/unsquashfs -d $PROG_EXT_LOC $ARCHIVE"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Testing Single-threaded Performance"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
hyperfine --warmup 5 --prepare "rm -rf $REF_EXT_LOC && rm -rf $PROG_EXT_LOC" "unsquashfs -p 1 -d $REF_EXT_LOC $ARCHIVE" "zig-out/bin/unsquashfs -p 1 -d $PROG_EXT_LOC $ARCHIVE"
|
||||||
|
|||||||
@@ -3,6 +3,10 @@ const std = @import("std");
|
|||||||
pub fn build(b: *std.Build) !void {
|
pub fn build(b: *std.Build) !void {
|
||||||
const use_zig_decomp = b.option(bool, "use_zig_decomp", "Use zig standard library for decompression.") orelse false;
|
const use_zig_decomp = b.option(bool, "use_zig_decomp", "Use zig standard library for decompression.") orelse false;
|
||||||
const allow_lzo = b.option(bool, "allow_lzo", "Compile with lzo support") orelse false;
|
const allow_lzo = b.option(bool, "allow_lzo", "Compile with lzo support") orelse false;
|
||||||
|
<<<<<<< HEAD
|
||||||
|
=======
|
||||||
|
const dynamic = b.option(bool, "dynamic", "Dynamicly link C decompression libraries") orelse false;
|
||||||
|
>>>>>>> dfbfbda (Build is working again (on Zig master branch))
|
||||||
var debug = b.option(bool, "debug", "Enable options to make debugging easier.");
|
var debug = b.option(bool, "debug", "Enable options to make debugging easier.");
|
||||||
const version_string_option = b.option([]const u8, "version", "Version of the library/binary");
|
const version_string_option = b.option([]const u8, "version", "Version of the library/binary");
|
||||||
|
|
||||||
@@ -11,9 +15,12 @@ pub fn build(b: *std.Build) !void {
|
|||||||
zig_squashfs_options.addOption(bool, "allow_lzo", allow_lzo);
|
zig_squashfs_options.addOption(bool, "allow_lzo", allow_lzo);
|
||||||
|
|
||||||
const target = b.standardTargetOptions(.{});
|
const target = b.standardTargetOptions(.{});
|
||||||
const optimize = b.standardOptimizeOption(.{});
|
var optimize = b.standardOptimizeOption(.{});
|
||||||
|
|
||||||
if (optimize == .Debug) debug = true;
|
if (debug == true)
|
||||||
|
optimize = .Debug;
|
||||||
|
if (optimize == .Debug)
|
||||||
|
debug = true;
|
||||||
|
|
||||||
const c = b.addTranslateC(.{
|
const c = b.addTranslateC(.{
|
||||||
.optimize = optimize,
|
.optimize = optimize,
|
||||||
@@ -28,18 +35,24 @@ pub fn build(b: *std.Build) !void {
|
|||||||
.target = target,
|
.target = target,
|
||||||
.valgrind = debug,
|
.valgrind = debug,
|
||||||
.root_source_file = b.path("src/root.zig"),
|
.root_source_file = b.path("src/root.zig"),
|
||||||
|
<<<<<<< HEAD
|
||||||
// .link_libc = true,
|
// .link_libc = true,
|
||||||
.imports = &.{
|
.imports = &.{
|
||||||
.{ .name = "options", .module = zig_squashfs_options.createModule() },
|
.{ .name = "options", .module = zig_squashfs_options.createModule() },
|
||||||
.{ .name = "c", .module = c.createModule() },
|
.{ .name = "c", .module = c.createModule() },
|
||||||
|
=======
|
||||||
|
.imports = &.{
|
||||||
|
.{ .name = "options", .module = zig_squashfs_options.createModule() },
|
||||||
|
>>>>>>> dfbfbda (Build is working again (on Zig master branch))
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
.use_llvm = debug,
|
.use_llvm = debug,
|
||||||
});
|
});
|
||||||
|
|
||||||
const zstd = b.dependency("zstd", .{ .optimize = optimize, .target = target });
|
const deps = try dependencies(b, optimize, target, use_zig_decomp, allow_lzo, dynamic);
|
||||||
lib.root_module.linkLibrary(zstd.artifact("zstd"));
|
defer b.allocator.free(deps);
|
||||||
|
|
||||||
|
<<<<<<< HEAD
|
||||||
const zng = b.dependency("zlib_ng", .{ .optimize = optimize, .target = target });
|
const zng = b.dependency("zlib_ng", .{ .optimize = optimize, .target = target });
|
||||||
lib.root_module.linkLibrary(zng.artifact("zng"));
|
lib.root_module.linkLibrary(zng.artifact("zng"));
|
||||||
|
|
||||||
@@ -51,6 +64,23 @@ pub fn build(b: *std.Build) !void {
|
|||||||
|
|
||||||
const lz4 = b.dependency("lz4", .{ .optimize = optimize, .target = target });
|
const lz4 = b.dependency("lz4", .{ .optimize = optimize, .target = target });
|
||||||
lib.root_module.linkLibrary(lz4.artifact("lz4"));
|
lib.root_module.linkLibrary(lz4.artifact("lz4"));
|
||||||
|
=======
|
||||||
|
for (deps) |d|
|
||||||
|
lib.root_module.linkLibrary(d);
|
||||||
|
|
||||||
|
if (!use_zig_decomp) {
|
||||||
|
const c = b.addTranslateC(.{
|
||||||
|
.optimize = optimize,
|
||||||
|
.target = target,
|
||||||
|
.root_source_file = b.path("src/c.h"),
|
||||||
|
});
|
||||||
|
if (allow_lzo) c.defineCMacro("ALLOW_LZO", null);
|
||||||
|
lib.root_module.addImport("c", c.createModule());
|
||||||
|
|
||||||
|
if (dynamic)
|
||||||
|
dynamicLinkLibraries(c, allow_lzo);
|
||||||
|
}
|
||||||
|
>>>>>>> dfbfbda (Build is working again (on Zig master branch))
|
||||||
|
|
||||||
var version = version_string_option orelse "0.0.0-testing";
|
var version = version_string_option orelse "0.0.0-testing";
|
||||||
if (version[0] == 'v') version = version[1..];
|
if (version[0] == 'v') version = version[1..];
|
||||||
@@ -80,17 +110,32 @@ pub fn build(b: *std.Build) !void {
|
|||||||
|
|
||||||
const mod_tests = b.addTest(.{
|
const mod_tests = b.addTest(.{
|
||||||
.root_module = b.createModule(.{
|
.root_module = b.createModule(.{
|
||||||
.optimize = .Debug,
|
.optimize = optimize,
|
||||||
.target = target,
|
.target = target,
|
||||||
.root_source_file = b.path("src/test.zig"),
|
.root_source_file = b.path("src/root.zig"),
|
||||||
.imports = &.{
|
.imports = &.{
|
||||||
.{ .name = "c", .module = c.createModule() },
|
.{ .name = "options", .module = zig_squashfs_options.createModule() },
|
||||||
},
|
},
|
||||||
.valgrind = true,
|
.valgrind = debug,
|
||||||
}),
|
}),
|
||||||
.use_llvm = true,
|
.use_llvm = debug,
|
||||||
});
|
});
|
||||||
mod_tests.root_module.linkLibrary(zstd.artifact("zstd"));
|
|
||||||
|
for (deps) |d|
|
||||||
|
mod_tests.root_module.linkLibrary(d);
|
||||||
|
|
||||||
|
if (!use_zig_decomp) {
|
||||||
|
const c = b.addTranslateC(.{
|
||||||
|
.optimize = optimize,
|
||||||
|
.target = target,
|
||||||
|
.root_source_file = b.path("src/c.h"),
|
||||||
|
});
|
||||||
|
mod_tests.root_module.addImport("c", c.createModule());
|
||||||
|
if (allow_lzo) c.defineCMacro("ALLOW_LZO", null);
|
||||||
|
|
||||||
|
if (dynamic)
|
||||||
|
dynamicLinkLibraries(c, allow_lzo);
|
||||||
|
}
|
||||||
|
|
||||||
const run_mod_tests = b.addRunArtifact(mod_tests);
|
const run_mod_tests = b.addRunArtifact(mod_tests);
|
||||||
const test_step = b.step("test", "Run tests");
|
const test_step = b.step("test", "Run tests");
|
||||||
@@ -109,3 +154,42 @@ pub fn build(b: *std.Build) !void {
|
|||||||
check.dependOn(&lib_check.step);
|
check.dependOn(&lib_check.step);
|
||||||
check.dependOn(&exe_check.step);
|
check.dependOn(&exe_check.step);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn dynamicLinkLibraries(mod: *std.Build.Step.TranslateC, allow_lzo: bool) void {
|
||||||
|
mod.linkSystemLibrary("zstd", .{});
|
||||||
|
mod.linkSystemLibrary("zlib-ng", .{});
|
||||||
|
mod.linkSystemLibrary("lzma", .{});
|
||||||
|
mod.linkSystemLibrary("lz4", .{});
|
||||||
|
if (allow_lzo)
|
||||||
|
mod.linkSystemLibrary("minilzo", .{});
|
||||||
|
}
|
||||||
|
fn dependencies(
|
||||||
|
b: *std.Build,
|
||||||
|
optimize: std.builtin.OptimizeMode,
|
||||||
|
target: std.Build.ResolvedTarget,
|
||||||
|
use_zig_decomp: bool,
|
||||||
|
allow_lzo: bool,
|
||||||
|
dynamic: bool,
|
||||||
|
) ![]*std.Build.Step.Compile {
|
||||||
|
if (use_zig_decomp or dynamic) return &.{};
|
||||||
|
|
||||||
|
var list: std.ArrayList(*std.Build.Step.Compile) = .empty;
|
||||||
|
|
||||||
|
const zstd = b.dependency("zstd", .{ .optimize = optimize, .target = target });
|
||||||
|
try list.append(b.allocator, zstd.artifact("zstd"));
|
||||||
|
|
||||||
|
const zng = b.dependency("zlib_ng", .{ .optimize = optimize, .target = target });
|
||||||
|
try list.append(b.allocator, zng.artifact("zng"));
|
||||||
|
|
||||||
|
const xz = b.dependency("xz", .{ .optimize = optimize, .target = target });
|
||||||
|
try list.append(b.allocator, xz.artifact("lzma"));
|
||||||
|
|
||||||
|
const lz4 = b.dependency("lz4", .{ .optimize = optimize, .target = target });
|
||||||
|
try list.append(b.allocator, lz4.artifact("lz4"));
|
||||||
|
|
||||||
|
if (allow_lzo) {
|
||||||
|
const minilzo = b.dependency("minilzo", .{ .optimize = optimize, .target = target });
|
||||||
|
try list.append(b.allocator, minilzo.artifact("minilzo"));
|
||||||
|
}
|
||||||
|
return list.toOwnedSlice(b.allocator);
|
||||||
|
}
|
||||||
|
|||||||
+133
-16
@@ -23,6 +23,7 @@ pub fn init(io: Io, file: std.Io.File, offset: u64) !Archive {
|
|||||||
try rdr.seekTo(offset);
|
try rdr.seekTo(offset);
|
||||||
var super: Superblock = undefined;
|
var super: Superblock = undefined;
|
||||||
try rdr.interface.readSliceEndian(Superblock, @ptrCast(&super), .little);
|
try rdr.interface.readSliceEndian(Superblock, @ptrCast(&super), .little);
|
||||||
|
try super.validate();
|
||||||
|
|
||||||
return .{
|
return .{
|
||||||
.file = try .init(io, file, super.size, offset),
|
.file = try .init(io, file, super.size, offset),
|
||||||
@@ -31,15 +32,14 @@ pub fn init(io: Io, file: std.Io.File, offset: u64) !Archive {
|
|||||||
.stateless_decomp = try Decomp.StatelessDecomp(super.compression),
|
.stateless_decomp = try Decomp.StatelessDecomp(super.compression),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
pub fn deinit(self: Archive, io: Io) void {
|
pub fn deinit(self: *Archive, io: Io) void {
|
||||||
self.file.deinit(io);
|
self.file.deinit(io);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The root folder of the Archive. Used to open other Files.
|
/// The root folder of the Archive. Used to open other Files.
|
||||||
pub fn root(self: Archive, alloc: std.mem.Allocator, io: Io) !File {
|
pub fn root(self: Archive, alloc: std.mem.Allocator) !File {
|
||||||
const root_inode = try Utils.inodeFromRef(
|
const root_inode = try Utils.inodeFromRef(
|
||||||
alloc,
|
alloc,
|
||||||
io,
|
|
||||||
self.file,
|
self.file,
|
||||||
self.stateless_decomp,
|
self.stateless_decomp,
|
||||||
self.super.inode_start,
|
self.super.inode_start,
|
||||||
@@ -50,7 +50,7 @@ pub fn root(self: Archive, alloc: std.mem.Allocator, io: Io) !File {
|
|||||||
}
|
}
|
||||||
/// Opens a File within the archive.
|
/// Opens a File within the archive.
|
||||||
pub fn open(self: Archive, alloc: std.mem.Allocator, io: Io, filepath: []const u8) !File {
|
pub fn open(self: Archive, alloc: std.mem.Allocator, io: Io, filepath: []const u8) !File {
|
||||||
const root_file = try self.root(alloc, io);
|
const root_file = try self.root(alloc);
|
||||||
const path = std.mem.trim(u8, filepath, "/");
|
const path = std.mem.trim(u8, filepath, "/");
|
||||||
if (Utils.pathIsSelf(path))
|
if (Utils.pathIsSelf(path))
|
||||||
return root_file;
|
return root_file;
|
||||||
@@ -95,6 +95,19 @@ pub fn idTable(self: Archive, alloc: std.mem.Allocator, io: Io, idx: u32) !u16 {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Extract the entire archive contents to the given directory.
|
||||||
|
pub fn extract(self: Archive, alloc: std.mem.Allocator, io: Io, extract_dir: []const u8, options: ExtractionOptions) !void {
|
||||||
|
const root_inode = try Utils.inodeFromRef(
|
||||||
|
alloc,
|
||||||
|
self.file,
|
||||||
|
self.stateless_decomp,
|
||||||
|
self.super.inode_start,
|
||||||
|
self.super.block_size,
|
||||||
|
self.super.root_ref,
|
||||||
|
);
|
||||||
|
return root_inode.extract(alloc, io, self.file, self.super, extract_dir, options);
|
||||||
|
}
|
||||||
|
|
||||||
// Superblock
|
// Superblock
|
||||||
|
|
||||||
const SQUASHFS_MAGIC: u32 = std.mem.readInt(u32, "hsqs", .little);
|
const SQUASHFS_MAGIC: u32 = std.mem.readInt(u32, "hsqs", .little);
|
||||||
@@ -155,17 +168,121 @@ pub const Superblock = extern struct {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Extraction
|
// Tests
|
||||||
|
|
||||||
/// Extract the entire archive contents to the given directory.
|
const TestArchive = "testing/LinuxPATest.sfs";
|
||||||
pub fn extract(self: Archive, alloc: std.mem.Allocator, io: Io, extract_dir: []const u8, options: ExtractionOptions) !void {
|
|
||||||
const root_inode = try Utils.inodeFromRef(
|
test "Basics" {
|
||||||
alloc,
|
std.debug.print("Starting test: Basics...\n", .{});
|
||||||
self.file,
|
|
||||||
self.stateless_decomp,
|
const alloc = std.testing.allocator;
|
||||||
self.super.inode_start,
|
const io = std.testing.io;
|
||||||
self.super.block_size,
|
|
||||||
self.super.root_ref,
|
var fil = try Io.Dir.cwd().openFile(io, TestArchive, .{});
|
||||||
);
|
defer fil.close(io);
|
||||||
return root_inode.extract(alloc, io, self.file, self.super, extract_dir, options);
|
var sfs: Archive = try .init(io, fil, 0);
|
||||||
|
defer sfs.deinit(io);
|
||||||
|
try std.testing.expectEqualDeep(sfs.super, LinuxPATestCorrectSuperblock);
|
||||||
|
const root_file = try sfs.root(alloc);
|
||||||
|
defer root_file.deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const TestFile = "Start.exe";
|
||||||
|
const TestFileExtractLocation = "testing/Start.exe";
|
||||||
|
|
||||||
|
test "ExtractSingleFile" {
|
||||||
|
std.debug.print("Starting test: ExtractSingleFile...\n", .{});
|
||||||
|
|
||||||
|
const alloc = std.testing.allocator;
|
||||||
|
const io = std.testing.io;
|
||||||
|
|
||||||
|
Io.Dir.cwd().deleteFile(io, TestFileExtractLocation) catch {};
|
||||||
|
var fil = try Io.Dir.cwd().openFile(io, TestArchive, .{});
|
||||||
|
defer fil.close(io);
|
||||||
|
var sfs: Archive = try .init(io, fil, 0);
|
||||||
|
defer sfs.deinit(io);
|
||||||
|
var test_fil = try sfs.open(alloc, io, TestFile);
|
||||||
|
defer test_fil.deinit();
|
||||||
|
try test_fil.extract(alloc, io, TestFileExtractLocation, .default);
|
||||||
|
//TODO: validate extracted file.
|
||||||
|
}
|
||||||
|
|
||||||
|
const TestFullExtractLocation = "testing/TestExtract";
|
||||||
|
|
||||||
|
test "ExtractCompleteArchive" {
|
||||||
|
std.debug.print("Starting test: ExtractCompleteArchive...\n", .{});
|
||||||
|
|
||||||
|
const alloc = std.testing.allocator;
|
||||||
|
const io = std.testing.io;
|
||||||
|
|
||||||
|
Io.Dir.cwd().deleteTree(io, TestFullExtractLocation) catch {};
|
||||||
|
var fil = try Io.Dir.cwd().openFile(io, TestArchive, .{});
|
||||||
|
defer fil.close(io);
|
||||||
|
var sfs: Archive = try .init(io, fil, 0);
|
||||||
|
defer sfs.deinit(io);
|
||||||
|
try sfs.extract(alloc, io, TestFullExtractLocation, .default);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "ExtractCompleteArchiveSingleThreaded" {
|
||||||
|
std.debug.print("Starting test: ExtractCompleteArchive...\n", .{});
|
||||||
|
|
||||||
|
const alloc = std.testing.allocator;
|
||||||
|
const io = std.testing.io;
|
||||||
|
|
||||||
|
var fil = try Io.Dir.cwd().openFile(io, TestArchive, .{});
|
||||||
|
defer fil.close(io);
|
||||||
|
{
|
||||||
|
std.debug.print("First testing using Threaded.global_single_threaded...\n", .{});
|
||||||
|
Io.Dir.cwd().deleteTree(io, TestFullExtractLocation) catch {};
|
||||||
|
var sfs: Archive = try .init(Io.Threaded.global_single_threaded.io(), fil, 0);
|
||||||
|
defer sfs.deinit(Io.Threaded.global_single_threaded.io());
|
||||||
|
try sfs.extract(alloc, Io.Threaded.global_single_threaded.io(), TestFullExtractLocation, .default);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
std.debug.print("Next testing using ExtractionOptions.single_threaded...\n", .{});
|
||||||
|
Io.Dir.cwd().deleteTree(io, TestFullExtractLocation) catch {};
|
||||||
|
var sfs: Archive = try .init(io, fil, 0);
|
||||||
|
defer sfs.deinit(io);
|
||||||
|
try sfs.extract(alloc, io, TestFullExtractLocation, .default_single_threaded);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const LinuxPATestCorrectSuperblock: Superblock = .{
|
||||||
|
.magic = std.mem.readInt(u32, "hsqs", .little),
|
||||||
|
.inode_count = 2974,
|
||||||
|
.mod_time = 1632696724,
|
||||||
|
.block_size = 131072,
|
||||||
|
.frag_count = 264,
|
||||||
|
.compression = .zstd,
|
||||||
|
.block_log = 17,
|
||||||
|
.flags = .{
|
||||||
|
.inode_uncompressed = false,
|
||||||
|
.data_uncompressed = false,
|
||||||
|
.check = false,
|
||||||
|
.frag_uncompressed = false,
|
||||||
|
.fragment_never = false,
|
||||||
|
.fragment_always = false,
|
||||||
|
.duplicates = true,
|
||||||
|
.exportable = true,
|
||||||
|
.xattr_uncompressed = false,
|
||||||
|
.xattr_never = false,
|
||||||
|
.compression_options = false,
|
||||||
|
.ids_uncompressed = false,
|
||||||
|
._ = 0,
|
||||||
|
},
|
||||||
|
.id_count = 1,
|
||||||
|
.ver_maj = 4,
|
||||||
|
.ver_min = 0,
|
||||||
|
.root_ref = .{
|
||||||
|
.block_offset = 1363,
|
||||||
|
.block_start = 29237,
|
||||||
|
._ = 0,
|
||||||
|
},
|
||||||
|
.size = 106841744,
|
||||||
|
.id_start = 106841632,
|
||||||
|
.xattr_start = 106841720,
|
||||||
|
.inode_start = 106778274,
|
||||||
|
.dir_start = 106807998,
|
||||||
|
.frag_start = 106837747,
|
||||||
|
.export_start = 106841602,
|
||||||
|
};
|
||||||
|
|||||||
+11
-1
@@ -60,6 +60,7 @@ pub fn main(init: std.process.Init) !void {
|
|||||||
|
|
||||||
var arc: squashfs.Archive = try .init(io, fil, offset); //TODO: Handle error gracefully.
|
var arc: squashfs.Archive = try .init(io, fil, offset); //TODO: Handle error gracefully.
|
||||||
const options: squashfs.ExtractionOptions = .{
|
const options: squashfs.ExtractionOptions = .{
|
||||||
|
.single_threaded = threads == 1,
|
||||||
.verbose = verbose,
|
.verbose = verbose,
|
||||||
.verbose_writer = if (verbose) &out.interface else null,
|
.verbose_writer = if (verbose) &out.interface else null,
|
||||||
.ignore_xattr = ignore_xattrs,
|
.ignore_xattr = ignore_xattrs,
|
||||||
@@ -68,7 +69,16 @@ pub fn main(init: std.process.Init) !void {
|
|||||||
|
|
||||||
if (force)
|
if (force)
|
||||||
try Io.Dir.cwd().deleteTree(io, extLoc);
|
try Io.Dir.cwd().deleteTree(io, extLoc);
|
||||||
try arc.extract(alloc, io, extLoc, options); //TODO: Handle error gracefully.
|
if (threads != 0) {
|
||||||
|
var limited_io = Io.Threaded.init(alloc, .{
|
||||||
|
.async_limit = .limited(threads - 1),
|
||||||
|
.concurrent_limit = .limited(threads - 1),
|
||||||
|
.argv0 = .init(init.minimal.args),
|
||||||
|
.environ = init.minimal.environ,
|
||||||
|
});
|
||||||
|
return arc.extract(alloc, limited_io.io(), extLoc, options); //TODO: Handle error gracefully.
|
||||||
|
}
|
||||||
|
return arc.extract(alloc, io, extLoc, options); //TODO: Handle error gracefully.
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handleArgs(args: std.process.Args, out: *Writer) !void {
|
fn handleArgs(args: std.process.Args, out: *Writer) !void {
|
||||||
|
|||||||
+35
-7
@@ -1,4 +1,5 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
const Io = std.Io;
|
||||||
|
|
||||||
const options = @import("options");
|
const options = @import("options");
|
||||||
|
|
||||||
@@ -7,7 +8,13 @@ const xz = @import("decomp/zig_xz.zig");
|
|||||||
const Decompressor = @import("util/decompressor.zig");
|
const Decompressor = @import("util/decompressor.zig");
|
||||||
|
|
||||||
const zlib = if (options.use_zig_decomp) @import("decomp/zig_zlib.zig") else @import("decomp/c_zlib.zig");
|
const zlib = if (options.use_zig_decomp) @import("decomp/zig_zlib.zig") else @import("decomp/c_zlib.zig");
|
||||||
|
<<<<<<< HEAD
|
||||||
const lzo = if (options.use_zig_decomp or !options.allow_lzo) void else @import("decomp/c_lzo.zig");
|
const lzo = if (options.use_zig_decomp or !options.allow_lzo) void else @import("decomp/c_lzo.zig");
|
||||||
|
=======
|
||||||
|
const lzma = if (options.use_zig_decomp) @import("decomp/zig_lzma.zig") else @import("decomp/c_lzma.zig");
|
||||||
|
const lzo = if (options.use_zig_decomp or options.allow_lzo) void else @import("decomp/c_lzo.zig");
|
||||||
|
const xz = if (options.use_zig_decomp) @import("decomp/zig_xz.zig") else @import("decomp/c_xz.zig");
|
||||||
|
>>>>>>> dfbfbda (Build is working again (on Zig master branch))
|
||||||
const lz4 = if (options.use_zig_decomp) void else @import("decomp/c_lz4.zig");
|
const lz4 = if (options.use_zig_decomp) void else @import("decomp/c_lz4.zig");
|
||||||
const zstd = if (options.use_zig_decomp) @import("decomp/zig_zstd.zig") else @import("decomp/c_zstd.zig");
|
const zstd = if (options.use_zig_decomp) @import("decomp/zig_zstd.zig") else @import("decomp/c_zstd.zig");
|
||||||
|
|
||||||
@@ -45,6 +52,7 @@ pub const Decomp = union(enum) {
|
|||||||
lz4: lz4,
|
lz4: lz4,
|
||||||
zstd: zstd,
|
zstd: zstd,
|
||||||
|
|
||||||
|
<<<<<<< HEAD
|
||||||
pub fn init(val: Enum, alloc: std.mem.Allocator, io: std.Io, block_size: u32) !Decomp {
|
pub fn init(val: Enum, alloc: std.mem.Allocator, io: std.Io, block_size: u32) !Decomp {
|
||||||
return switch (val) {
|
return switch (val) {
|
||||||
.gzip => .{ .gzip = zlib.init(alloc, io, block_size) },
|
.gzip => .{ .gzip = zlib.init(alloc, io, block_size) },
|
||||||
@@ -53,13 +61,33 @@ pub const Decomp = union(enum) {
|
|||||||
.xz => .{ .xz = .{} },
|
.xz => .{ .xz = .{} },
|
||||||
.lz4 => .{ .lz4 = .{} },
|
.lz4 => .{ .lz4 = .{} },
|
||||||
.zstd => .{ .zstd = zstd.init(alloc, io, block_size) },
|
.zstd => .{ .zstd = zstd.init(alloc, io, block_size) },
|
||||||
|
=======
|
||||||
|
pub fn init(val: Enum, alloc: std.mem.Allocator, io: Io, block_size: u32) !Decomp {
|
||||||
|
return switch (val) {
|
||||||
|
.gzip => .{ .gzip = if (options.use_zig_decomp) try zlib.init(alloc, io, block_size) else try zlib.init(alloc, io) },
|
||||||
|
.lzma => .{ .lzma = if (options.use_zig_decomp) try lzma.init(alloc, io, block_size) else .{} },
|
||||||
|
.lzo => if (options.use_zig_decomp or !options.allow_lzo) error.LzoUnsupported else .{ .lzo = .{} },
|
||||||
|
.xz => .{ .xz = if (options.use_zig_decomp) try xz.init(alloc, io, block_size) else .{} },
|
||||||
|
.lz4 => if (options.use_zig_decomp) error.Lz4Unsupported else .{ .lz4 = .{} },
|
||||||
|
.zstd => .{ .zstd = if (options.use_zig_decomp) try zstd.init(alloc, io, block_size) else try zstd.init(alloc, io) },
|
||||||
|
>>>>>>> dfbfbda (Build is working again (on Zig master branch))
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
pub fn deinit(self: *Decomp) void {
|
pub fn deinit(self: *Decomp, alloc: std.mem.Allocator) void {
|
||||||
switch (self.*) {
|
if (options.use_zig_decomp) {
|
||||||
.gzip => self.gzip.deinit(),
|
switch (self.*) {
|
||||||
.zstd => self.zstd.deinit(),
|
.gzip => self.gzip.deinit(),
|
||||||
else => {},
|
.lzma => self.lzma.deinit(),
|
||||||
|
.xz => self.xz.deinit(),
|
||||||
|
.zstd => self.zstd.deinit(),
|
||||||
|
else => {},
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch (self.*) {
|
||||||
|
.gzip => self.gzip.deinit(alloc),
|
||||||
|
.zstd => self.zstd.deinit(alloc),
|
||||||
|
else => {},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,9 +95,9 @@ pub const Decomp = union(enum) {
|
|||||||
return switch (self.*) {
|
return switch (self.*) {
|
||||||
.gzip => &self.gzip.interface,
|
.gzip => &self.gzip.interface,
|
||||||
.lzma => &lzma.stateless_decompressor,
|
.lzma => &lzma.stateless_decompressor,
|
||||||
.lzo => &lzo.stateless_decompressor,
|
.lzo => if (options.use_zig_decomp or !options.allow_lzo) unreachable else &lzo.stateless_decompressor,
|
||||||
.xz => &xz.stateless_decompressor,
|
.xz => &xz.stateless_decompressor,
|
||||||
.lz4 => &lz4.stateless_decompressor,
|
.lz4 => if (options.use_zig_decomp) unreachable else &lz4.stateless_decompressor,
|
||||||
.zstd => &self.zstd.interface,
|
.zstd => &self.zstd.interface,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-11
@@ -8,18 +8,12 @@ const Error = Decompressor.Error;
|
|||||||
pub const stateless_decompressor: Decompressor = .{ .decomp_fn = statelessDecomp };
|
pub const stateless_decompressor: Decompressor = .{ .decomp_fn = statelessDecomp };
|
||||||
|
|
||||||
fn statelessDecomp(_: ?*const Decompressor, _: std.mem.Allocator, in: []u8, out: []u8) Error!usize {
|
fn statelessDecomp(_: ?*const Decompressor, _: std.mem.Allocator, in: []u8, out: []u8) Error!usize {
|
||||||
|
<<<<<<< HEAD
|
||||||
const res = c.LZ4_decompress_fast(in.ptr, out.ptr, @truncate(out.len));
|
const res = c.LZ4_decompress_fast(in.ptr, out.ptr, @truncate(out.len));
|
||||||
|
=======
|
||||||
|
const out_len: c_int = @bitCast(@as(u32, @truncate(out.len)));
|
||||||
|
const res = c.LZ4_decompress_fast(in.ptr, out.ptr, out_len);
|
||||||
|
>>>>>>> dfbfbda (Build is working again (on Zig master branch))
|
||||||
if (res < 0) return Error.ReadFailed;
|
if (res < 0) return Error.ReadFailed;
|
||||||
return @abs(res);
|
return @abs(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
// lzma_allocator
|
|
||||||
|
|
||||||
fn lzmaAlloc(ptr: ?*anyopaque, size: usize, _: usize) callconv(.c) ?*anyopaque {
|
|
||||||
var alloc: *std.mem.Allocator = @ptrCast(@alignCast(@constCast(ptr)));
|
|
||||||
return alloc.rawAlloc(size, .@"1", 0);
|
|
||||||
}
|
|
||||||
fn lzmaFree(ptr: ?*anyopaque, mem_ptr: ?*anyopaque) callconv(.c) void {
|
|
||||||
var alloc: *std.mem.Allocator = @ptrCast(@alignCast(@constCast(ptr)));
|
|
||||||
alloc.rawFree(@ptrCast(mem_ptr), .@"1", 0);
|
|
||||||
}
|
|
||||||
|
|||||||
+10
-14
@@ -15,13 +15,8 @@ const Self = @This();
|
|||||||
|
|
||||||
pub const stateless_decompressor: Decompressor = .{ .decomp_fn = statelessDecomp };
|
pub const stateless_decompressor: Decompressor = .{ .decomp_fn = statelessDecomp };
|
||||||
|
|
||||||
fn statelessDecomp(_: ?*const Decompressor, alloc: std.mem.Allocator, in: []u8, out: []u8) Error!usize {
|
fn statelessDecomp(_: ?*const Decompressor, _: std.mem.Allocator, in: []u8, out: []u8) Error!usize {
|
||||||
var stream: c.lzma_stream = .{
|
var stream: c.lzma_stream = .{
|
||||||
.allocator = &.{
|
|
||||||
.alloc = lzmaAlloc,
|
|
||||||
.free = lzmaFree,
|
|
||||||
.@"opaque" = @constCast(&alloc),
|
|
||||||
},
|
|
||||||
.next_in = in.ptr,
|
.next_in = in.ptr,
|
||||||
.avail_in = in.len,
|
.avail_in = in.len,
|
||||||
.next_out = out.ptr,
|
.next_out = out.ptr,
|
||||||
@@ -38,11 +33,12 @@ fn statelessDecomp(_: ?*const Decompressor, alloc: std.mem.Allocator, in: []u8,
|
|||||||
|
|
||||||
// lzma_allocator
|
// lzma_allocator
|
||||||
|
|
||||||
fn lzmaAlloc(ptr: ?*anyopaque, size: usize, _: usize) callconv(.c) ?*anyopaque {
|
// fn lzmaAlloc(ptr: ?*anyopaque, size: usize, _: usize) callconv(.c) ?*anyopaque {
|
||||||
var alloc: *std.mem.Allocator = @ptrCast(@alignCast(@constCast(ptr)));
|
// var alloc: *std.mem.Allocator = @ptrCast(@alignCast(ptr));
|
||||||
return alloc.rawAlloc(size, .@"1", 0);
|
// return alloc.rawAlloc(size, .@"1", 0);
|
||||||
}
|
// }
|
||||||
fn lzmaFree(ptr: ?*anyopaque, mem_ptr: ?*anyopaque) callconv(.c) void {
|
// fn lzmaFree(ptr: ?*anyopaque, mem_ptr: ?*anyopaque) callconv(.c) void {
|
||||||
var alloc: *std.mem.Allocator = @ptrCast(@alignCast(@constCast(ptr)));
|
// if (mem_ptr == null) return;
|
||||||
alloc.rawFree(@ptrCast(mem_ptr), .@"1", 0);
|
// var alloc: *std.mem.Allocator = @ptrCast(@alignCast(ptr));
|
||||||
}
|
// alloc.free(@as([*]u8, @ptrCast(mem_ptr.?)));
|
||||||
|
// }
|
||||||
|
|||||||
@@ -22,14 +22,3 @@ fn statelessDecomp(_: ?*const Decompressor, _: std.mem.Allocator, in: []u8, out:
|
|||||||
if (res != c.LZO_E_OK) return Error.ReadFailed;
|
if (res != c.LZO_E_OK) return Error.ReadFailed;
|
||||||
return out_len;
|
return out_len;
|
||||||
}
|
}
|
||||||
|
|
||||||
// lzma_allocator
|
|
||||||
|
|
||||||
fn lzmaAlloc(ptr: ?*anyopaque, size: usize, _: usize) callconv(.c) ?*anyopaque {
|
|
||||||
var alloc: *std.mem.Allocator = @ptrCast(@alignCast(@constCast(ptr)));
|
|
||||||
return alloc.rawAlloc(size, .@"1", 0);
|
|
||||||
}
|
|
||||||
fn lzmaFree(ptr: ?*anyopaque, mem_ptr: ?*anyopaque) callconv(.c) void {
|
|
||||||
var alloc: *std.mem.Allocator = @ptrCast(@alignCast(@constCast(ptr)));
|
|
||||||
alloc.rawFree(@ptrCast(mem_ptr), .@"1", 0);
|
|
||||||
}
|
|
||||||
|
|||||||
+11
-14
@@ -15,13 +15,8 @@ const Self = @This();
|
|||||||
|
|
||||||
pub const stateless_decompressor: Decompressor = .{ .decomp_fn = statelessDecomp };
|
pub const stateless_decompressor: Decompressor = .{ .decomp_fn = statelessDecomp };
|
||||||
|
|
||||||
fn statelessDecomp(_: ?*const Decompressor, alloc: std.mem.Allocator, in: []u8, out: []u8) Error!usize {
|
fn statelessDecomp(_: ?*const Decompressor, _: std.mem.Allocator, in: []u8, out: []u8) Error!usize {
|
||||||
var stream: c.lzma_stream = .{
|
var stream: c.lzma_stream = .{
|
||||||
.allocator = &.{
|
|
||||||
.alloc = lzmaAlloc,
|
|
||||||
.free = lzmaFree,
|
|
||||||
.@"opaque" = @constCast(&alloc),
|
|
||||||
},
|
|
||||||
.next_in = in.ptr,
|
.next_in = in.ptr,
|
||||||
.avail_in = in.len,
|
.avail_in = in.len,
|
||||||
.next_out = out.ptr,
|
.next_out = out.ptr,
|
||||||
@@ -38,11 +33,13 @@ fn statelessDecomp(_: ?*const Decompressor, alloc: std.mem.Allocator, in: []u8,
|
|||||||
|
|
||||||
// lzma_allocator
|
// lzma_allocator
|
||||||
|
|
||||||
fn lzmaAlloc(ptr: ?*anyopaque, size: usize, _: usize) callconv(.c) ?*anyopaque {
|
// fn lzmaAlloc(ptr: ?*anyopaque, size: usize, _: usize) callconv(.c) ?*anyopaque {
|
||||||
var alloc: *std.mem.Allocator = @ptrCast(@alignCast(@constCast(ptr)));
|
// var alloc: *std.mem.Allocator = @ptrCast(@alignCast(ptr));
|
||||||
return alloc.rawAlloc(size, .@"1", 0);
|
// const mem = alloc.alloc(u8, size) catch return null;
|
||||||
}
|
// return mem.ptr;
|
||||||
fn lzmaFree(ptr: ?*anyopaque, mem_ptr: ?*anyopaque) callconv(.c) void {
|
// }
|
||||||
var alloc: *std.mem.Allocator = @ptrCast(@alignCast(@constCast(ptr)));
|
// fn lzmaFree(ptr: ?*anyopaque, mem_ptr: ?*anyopaque) callconv(.c) void {
|
||||||
alloc.rawFree(@ptrCast(mem_ptr), .@"1", 0);
|
// if (mem_ptr == null) return;
|
||||||
}
|
// var alloc: *std.mem.Allocator = @ptrCast(@alignCast(ptr));
|
||||||
|
// alloc.free(@as([*]u8, @ptrCast(mem_ptr.?)));
|
||||||
|
// }
|
||||||
|
|||||||
+10
-11
@@ -20,20 +20,15 @@ io: Io,
|
|||||||
ctx: []c.zng_stream,
|
ctx: []c.zng_stream,
|
||||||
ctx_queue: Queue,
|
ctx_queue: Queue,
|
||||||
|
|
||||||
pub fn init(alloc: std.mem.Allocator, io: Io, block_size: u32) !Self {
|
pub fn init(alloc: std.mem.Allocator, io: Io) !Self {
|
||||||
const buf = try alloc.alloc(c.zng_stream, 20); // TODO: Choose a better number instead of a random one.
|
const buf = try alloc.alloc(c.zng_stream, 20); // TODO: Choose a better number instead of a random one.
|
||||||
var queue: Queue = .init(buf);
|
var queue: Queue = .init(buf);
|
||||||
for (0..20) |_|
|
for (0..20) |_|
|
||||||
try queue.putOne(io, .{
|
try queue.putOne(io, .{});
|
||||||
.zalloc = zalloc,
|
|
||||||
.zfree = zfree,
|
|
||||||
});
|
|
||||||
|
|
||||||
return .{
|
return .{
|
||||||
.alloc = alloc,
|
|
||||||
.io = io,
|
.io = io,
|
||||||
|
|
||||||
.block_size = block_size,
|
|
||||||
.ctx = buf,
|
.ctx = buf,
|
||||||
.ctx_queue = queue,
|
.ctx_queue = queue,
|
||||||
};
|
};
|
||||||
@@ -52,13 +47,12 @@ fn decomp(d: ?*const Decompressor, alloc: std.mem.Allocator, in: []u8, out: []u8
|
|||||||
var stream = self.ctx_queue.getOne(self.io) catch return Error.ReadFailed;
|
var stream = self.ctx_queue.getOne(self.io) catch return Error.ReadFailed;
|
||||||
defer self.ctx_queue.putOne(self.io, stream) catch {};
|
defer self.ctx_queue.putOne(self.io, stream) catch {};
|
||||||
|
|
||||||
stream.@"opaque" = @constCast(&alloc);
|
|
||||||
stream.next_in = in.ptr;
|
stream.next_in = in.ptr;
|
||||||
stream.avail_in = @truncate(in.len);
|
stream.avail_in = @truncate(in.len);
|
||||||
stream.next_out = out.ptr;
|
stream.next_out = out.ptr;
|
||||||
stream.avail_out = @truncate(out.len);
|
stream.avail_out = @truncate(out.len);
|
||||||
|
|
||||||
try zlibDecomp(&stream, in, out);
|
try zlibDecomp(&stream);
|
||||||
|
|
||||||
return stream.total_out;
|
return stream.total_out;
|
||||||
}
|
}
|
||||||
@@ -74,9 +68,8 @@ inline fn zlibDecomp(stream: *c.zng_stream) !void {
|
|||||||
|
|
||||||
pub const stateless_decompressor: Decompressor = .{ .decomp_fn = statelessDecomp };
|
pub const stateless_decompressor: Decompressor = .{ .decomp_fn = statelessDecomp };
|
||||||
|
|
||||||
fn statelessDecomp(_: ?*const Decompressor, alloc: std.mem.Allocator, in: []u8, out: []u8) Error!usize {
|
fn statelessDecomp(_: ?*const Decompressor, _: std.mem.Allocator, in: []u8, out: []u8) Error!usize {
|
||||||
var stream: c.zng_stream = .{
|
var stream: c.zng_stream = .{
|
||||||
.@"opaque" = @constCast(&alloc),
|
|
||||||
.next_in = in.ptr,
|
.next_in = in.ptr,
|
||||||
.avail_in = @truncate(in.len),
|
.avail_in = @truncate(in.len),
|
||||||
.next_out = out.ptr,
|
.next_out = out.ptr,
|
||||||
@@ -93,6 +86,12 @@ fn zalloc(ptr: ?*anyopaque, size: c_uint, len: c_uint) callconv(.c) ?*anyopaque
|
|||||||
return alloc.rawAlloc(size * len, .@"1", 0);
|
return alloc.rawAlloc(size * len, .@"1", 0);
|
||||||
}
|
}
|
||||||
fn zfree(ptr: ?*anyopaque, mem_ptr: ?*anyopaque) callconv(.c) void {
|
fn zfree(ptr: ?*anyopaque, mem_ptr: ?*anyopaque) callconv(.c) void {
|
||||||
|
<<<<<<< HEAD
|
||||||
var alloc: *std.mem.Allocator = @ptrCast(@alignCast(ptr));
|
var alloc: *std.mem.Allocator = @ptrCast(@alignCast(ptr));
|
||||||
alloc.rawFree(@ptrCast(mem_ptr), .@"1", 0);
|
alloc.rawFree(@ptrCast(mem_ptr), .@"1", 0);
|
||||||
|
=======
|
||||||
|
if (mem_ptr == null) return;
|
||||||
|
var alloc: *std.mem.Allocator = @ptrCast(@alignCast(ptr));
|
||||||
|
alloc.free(@as([*]u8, @ptrCast(mem_ptr.?)));
|
||||||
|
>>>>>>> dfbfbda (Build is working again (on Zig master branch))
|
||||||
}
|
}
|
||||||
|
|||||||
+15
-15
@@ -20,17 +20,15 @@ io: Io,
|
|||||||
ctx: []?*c.ZSTD_DCtx,
|
ctx: []?*c.ZSTD_DCtx,
|
||||||
ctx_queue: Queue,
|
ctx_queue: Queue,
|
||||||
|
|
||||||
pub fn init(alloc: std.mem.Allocator, io: Io, block_size: u32) !Self {
|
pub fn init(alloc: std.mem.Allocator, io: Io) !Self {
|
||||||
const buf = try alloc.alloc(?*c.ZSTD_DCtx, 20); // TODO: Choose a better number instead of a random one.
|
const buf = try alloc.alloc(?*c.ZSTD_DCtx, 20); // TODO: Choose a better number instead of a random one.
|
||||||
var queue: Queue = .init(buf);
|
var queue: Queue = .init(buf);
|
||||||
for (0..20) |_|
|
for (0..20) |_|
|
||||||
try queue.putOne(io, c.ZSTD_createDCtx());
|
try queue.putOne(io, c.ZSTD_createDCtx());
|
||||||
|
|
||||||
return .{
|
return .{
|
||||||
.alloc = alloc,
|
|
||||||
.io = io,
|
.io = io,
|
||||||
|
|
||||||
.block_size = block_size,
|
|
||||||
.ctx = buf,
|
.ctx = buf,
|
||||||
.ctx_queue = queue,
|
.ctx_queue = queue,
|
||||||
};
|
};
|
||||||
@@ -38,25 +36,27 @@ pub fn init(alloc: std.mem.Allocator, io: Io, block_size: u32) !Self {
|
|||||||
pub fn deinit(self: *Self, alloc: std.mem.Allocator) void {
|
pub fn deinit(self: *Self, alloc: std.mem.Allocator) void {
|
||||||
self.ctx_queue.close(self.io);
|
self.ctx_queue.close(self.io);
|
||||||
for (self.ctx) |ctx|
|
for (self.ctx) |ctx|
|
||||||
c.ZSTD_freeDCtx(ctx);
|
_ = c.ZSTD_freeDCtx(ctx);
|
||||||
alloc.free(self.ctx);
|
alloc.free(self.ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decomp(d: ?*const Decompressor, alloc: std.mem.Allocator, in: []u8, out: []u8) Error!usize {
|
fn decomp(d: ?*const Decompressor, alloc: std.mem.Allocator, in: []u8, out: []u8) Error!usize {
|
||||||
if (d == null) {
|
// TODO: Fix
|
||||||
return statelessDecomp(d, alloc, in, out);
|
//
|
||||||
}
|
// if (d == null) {
|
||||||
var self: *Self = @fieldParentPtr("interface", @constCast(d.?));
|
return statelessDecomp(d, alloc, in, out);
|
||||||
|
// }
|
||||||
|
// var self: *Self = @fieldParentPtr("interface", @constCast(d.?));
|
||||||
|
|
||||||
const ctx = self.ctx_queue.getOne(self.io) catch return Error.ReadFailed;
|
// const ctx = self.ctx_queue.getOne(self.io) catch return Error.ReadFailed;
|
||||||
defer self.ctx_queue.putOne(self.io, ctx) catch {};
|
// defer self.ctx_queue.putOne(self.io, ctx) catch {};
|
||||||
|
|
||||||
_ = c.ZSTD_DCtx_reset(ctx, c.ZSTD_reset_session_only);
|
// _ = c.ZSTD_DCtx_reset(ctx, c.ZSTD_reset_session_only);
|
||||||
|
|
||||||
const res = c.ZSTD_decompressDCtx(ctx, out.ptr, out.len, in.ptr, in.len);
|
// const res = c.ZSTD_decompressDCtx(ctx, out.ptr, out.len, in.ptr, in.len);
|
||||||
if (c.ZSTD_isError(res) != 0)
|
// if (c.ZSTD_isError(res) != 0)
|
||||||
return Error.ReadFailed;
|
// return Error.ReadFailed;
|
||||||
return res;
|
// return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stateless
|
// Stateless
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ fn decomp(d: ?*const Decompressor, alloc: std.mem.Allocator, in: []u8, out: []u8
|
|||||||
const buf = self.buf_queue.getOne(self.io) catch return Error.ReadFailed;
|
const buf = self.buf_queue.getOne(self.io) catch return Error.ReadFailed;
|
||||||
defer self.buf_queue.putOne(self.io, buf) catch {};
|
defer self.buf_queue.putOne(self.io, buf) catch {};
|
||||||
|
|
||||||
return zlibDecomp(buf.buf, in, out);
|
return zlibDecomp(buf, in, out);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fn zlibDecomp(buffer: []u8, in: []u8, out: []u8) !usize {
|
inline fn zlibDecomp(buffer: []u8, in: []u8, out: []u8) !usize {
|
||||||
|
|||||||
@@ -20,9 +20,9 @@ buf: [][]u8,
|
|||||||
buf_queue: Queue,
|
buf_queue: Queue,
|
||||||
|
|
||||||
pub fn init(alloc: std.mem.Allocator, io: Io, block_size: u32) !Self {
|
pub fn init(alloc: std.mem.Allocator, io: Io, block_size: u32) !Self {
|
||||||
const buf = try alloc.alloc([]u8, 20); // TODO: Choose a better number instead of a random one.
|
const buf = try alloc.alloc([]u8, 5); // TODO: Choose a better number instead of a random one.
|
||||||
var queue: Queue = .init(buf);
|
var queue: Queue = .init(buf);
|
||||||
for (0..20) |_|
|
for (buf) |_|
|
||||||
try queue.putOne(io, try alloc.alloc(u8, block_size + zstd.block_size_max));
|
try queue.putOne(io, try alloc.alloc(u8, block_size + zstd.block_size_max));
|
||||||
|
|
||||||
return .{
|
return .{
|
||||||
|
|||||||
+2
-3
@@ -37,7 +37,7 @@ pub fn init(alloc: std.mem.Allocator, archive: Archive, in: Inode, name: []const
|
|||||||
}
|
}
|
||||||
pub fn fromDirEntry(alloc: std.mem.Allocator, archive: Archive, ent: DirEntry) !File {
|
pub fn fromDirEntry(alloc: std.mem.Allocator, archive: Archive, ent: DirEntry) !File {
|
||||||
var rdr = archive.file.readerAt(archive.super.inode_start + ent.block_start);
|
var rdr = archive.file.readerAt(archive.super.inode_start + ent.block_start);
|
||||||
var meta: MetadataReader = .init(alloc, &rdr.interface, archive.stateless_decomp);
|
var meta: MetadataReader = .init(alloc, &rdr, archive.stateless_decomp);
|
||||||
try meta.interface.discardAll(ent.block_offset);
|
try meta.interface.discardAll(ent.block_offset);
|
||||||
|
|
||||||
var in: Inode = try .read(alloc, &meta.interface, archive.super.block_size);
|
var in: Inode = try .read(alloc, &meta.interface, archive.super.block_size);
|
||||||
@@ -52,7 +52,6 @@ pub fn deinit(self: File) void {
|
|||||||
pub fn open(self: File, alloc: std.mem.Allocator, io: Io, filepath: []const u8) !File {
|
pub fn open(self: File, alloc: std.mem.Allocator, io: Io, filepath: []const u8) !File {
|
||||||
const entries = try self.inode.readDirectory(
|
const entries = try self.inode.readDirectory(
|
||||||
alloc,
|
alloc,
|
||||||
io,
|
|
||||||
self.archive.file,
|
self.archive.file,
|
||||||
self.archive.stateless_decomp,
|
self.archive.stateless_decomp,
|
||||||
self.archive.super.dir_start,
|
self.archive.super.dir_start,
|
||||||
@@ -77,7 +76,7 @@ pub fn open(self: File, alloc: std.mem.Allocator, io: Io, filepath: []const u8)
|
|||||||
}
|
}
|
||||||
} else return Error.FileNotFound;
|
} else return Error.FileNotFound;
|
||||||
|
|
||||||
const first_elem_file = try fromDirEntry(alloc, io, self.archive, search_slice[idx]);
|
const first_elem_file = try fromDirEntry(alloc, self.archive, search_slice[idx]);
|
||||||
if (first_element.len == path.len)
|
if (first_element.len == path.len)
|
||||||
return first_elem_file;
|
return first_elem_file;
|
||||||
defer first_elem_file.deinit();
|
defer first_elem_file.deinit();
|
||||||
|
|||||||
+235
-56
@@ -276,73 +276,26 @@ pub fn extract(
|
|||||||
const path = std.mem.trimEnd(u8, filepath, "/");
|
const path = std.mem.trimEnd(u8, filepath, "/");
|
||||||
|
|
||||||
var decomp_base: Decomp = try .init(super.compression, alloc, io, super.block_size);
|
var decomp_base: Decomp = try .init(super.compression, alloc, io, super.block_size);
|
||||||
|
decomp_base.deinit(alloc);
|
||||||
const decomp = decomp_base.decompressor();
|
const decomp = decomp_base.decompressor();
|
||||||
|
|
||||||
var frag_mgr: FragManager = try .init(alloc, fil, decomp, super.frag_start, super.frag_count, super.block_size);
|
var frag_mgr: FragManager = try .init(alloc, fil, decomp, super.frag_start, super.frag_count, super.block_size);
|
||||||
defer frag_mgr.deinit(io);
|
defer frag_mgr.deinit(io);
|
||||||
|
|
||||||
|
if (options.single_threaded)
|
||||||
|
return self.extractSinglethreaded(alloc, io, fil, super, path, options, decomp, &frag_mgr);
|
||||||
|
|
||||||
var sel_buf: [10]ExtractReturnUnion = undefined;
|
var sel_buf: [10]ExtractReturnUnion = undefined;
|
||||||
var sel: Io.Select(ExtractReturnUnion) = .init(io, &sel_buf);
|
var sel: Io.Select(ExtractReturnUnion) = .init(io, &sel_buf);
|
||||||
defer sel.cancelDiscard();
|
defer sel.cancelDiscard();
|
||||||
|
|
||||||
sel.async(.path_ret, extractReal, .{ self, alloc, io, fil, super, decomp, &sel, &frag_mgr, path, true });
|
var loop = io.async(finishLoop, .{ alloc, io, fil, decomp, super, options, &sel });
|
||||||
|
|
||||||
var id_table: CachedTable(u16) = .init(alloc, fil, decomp, super.id_start, super.id_count);
|
sel.async(.path_ret, extractRealAsync, .{ self, alloc, io, fil, super, decomp, &sel, &frag_mgr, path, true });
|
||||||
defer id_table.deinit(io);
|
|
||||||
|
|
||||||
var xattr_table: ?XattrTable = if (super.flags.xattr_never or options.ignore_xattr or !@hasField(std.os, "linux"))
|
try loop.await(io);
|
||||||
null
|
|
||||||
else
|
|
||||||
try .init(alloc, fil, decomp, super.xattr_start);
|
|
||||||
defer if (xattr_table != null) xattr_table.?.deinit(io);
|
|
||||||
|
|
||||||
var dir_queue: std.PriorityDequeue(PathRet, void, DirCompare) = .empty;
|
|
||||||
defer dir_queue.deinit(alloc);
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
if (sel.group.token.load(.unordered) == null) break;
|
|
||||||
|
|
||||||
const ret = try sel.await();
|
|
||||||
const path_ret = try ret.path_ret;
|
|
||||||
|
|
||||||
if (options.ignore_permissions and xattr_table == null) {
|
|
||||||
path_ret.deinit(alloc);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (path_ret.inode.hdr.inode_type == .dir or path_ret.inode.hdr.inode_type == .ext_dir) {
|
|
||||||
try dir_queue.push(alloc, path_ret);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
defer path_ret.deinit(alloc);
|
|
||||||
try path_ret.setMetadata(alloc, io, &id_table, if (xattr_table == null) null else &xattr_table.?, options);
|
|
||||||
}
|
|
||||||
|
|
||||||
while (sel.cancel()) |ret| {
|
|
||||||
const path_ret = try ret.path_ret;
|
|
||||||
|
|
||||||
if (options.ignore_permissions and xattr_table == null) {
|
|
||||||
path_ret.deinit(alloc);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (path_ret.inode.hdr.inode_type == .dir or path_ret.inode.hdr.inode_type == .ext_dir) {
|
|
||||||
try dir_queue.push(alloc, path_ret);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
defer path_ret.deinit(alloc);
|
|
||||||
try path_ret.setMetadata(alloc, io, &id_table, if (xattr_table == null) null else &xattr_table.?, options);
|
|
||||||
}
|
|
||||||
|
|
||||||
var iter = dir_queue.iterator();
|
|
||||||
while (iter.next()) |path_ret| {
|
|
||||||
defer path_ret.deinit(alloc);
|
|
||||||
try path_ret.setMetadata(alloc, io, &id_table, if (xattr_table == null) null else &xattr_table.?, options);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
pub fn extractReal(
|
fn extractRealAsync(
|
||||||
self: Inode,
|
self: Inode,
|
||||||
alloc: std.mem.Allocator,
|
alloc: std.mem.Allocator,
|
||||||
io: Io,
|
io: Io,
|
||||||
@@ -385,7 +338,7 @@ pub fn extractReal(
|
|||||||
const new_inode = try read(alloc, &meta.interface, super.block_size);
|
const new_inode = try read(alloc, &meta.interface, super.block_size);
|
||||||
errdefer new_inode.deinit(alloc);
|
errdefer new_inode.deinit(alloc);
|
||||||
|
|
||||||
sel.async(.path_ret, extractReal, .{ new_inode, alloc, io, fil, super, decomp, sel, frag_mgr, new_path, false });
|
sel.async(.path_ret, extractRealAsync, .{ new_inode, alloc, io, fil, super, decomp, sel, frag_mgr, new_path, false });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.file, .ext_file => {
|
.file, .ext_file => {
|
||||||
@@ -454,3 +407,229 @@ pub fn extractReal(
|
|||||||
.origin = origin,
|
.origin = origin,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
fn finishLoop(alloc: std.mem.Allocator, io: Io, fil: OffsetFile, decomp: *const Decompressor, super: Archive.Superblock, options: ExtractionOptions, sel: *Io.Select(ExtractReturnUnion)) !void {
|
||||||
|
var id_table: CachedTable(u16) = .init(alloc, fil, decomp, super.id_start, super.id_count);
|
||||||
|
defer id_table.deinit(io);
|
||||||
|
|
||||||
|
var xattr_table: ?XattrTable = if (super.flags.xattr_never or options.ignore_xattr or !@hasField(std.os, "linux"))
|
||||||
|
null
|
||||||
|
else
|
||||||
|
try .init(alloc, fil, decomp, super.xattr_start);
|
||||||
|
defer if (xattr_table != null) xattr_table.?.deinit(io);
|
||||||
|
|
||||||
|
var dir_queue: std.PriorityDequeue(PathRet, void, DirCompare) = .empty;
|
||||||
|
defer dir_queue.deinit(alloc);
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
if (sel.group.token.load(.unordered) == null) break;
|
||||||
|
|
||||||
|
const ret = try sel.await();
|
||||||
|
const path_ret = try ret.path_ret;
|
||||||
|
|
||||||
|
if (options.ignore_permissions and xattr_table == null) {
|
||||||
|
path_ret.deinit(alloc);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path_ret.inode.hdr.inode_type == .dir or path_ret.inode.hdr.inode_type == .ext_dir) {
|
||||||
|
try dir_queue.push(alloc, path_ret);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
defer path_ret.deinit(alloc);
|
||||||
|
|
||||||
|
try path_ret.setMetadata(alloc, io, &id_table, if (xattr_table == null) null else &xattr_table.?, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (sel.cancel()) |ret| {
|
||||||
|
const path_ret = try ret.path_ret;
|
||||||
|
|
||||||
|
if (options.ignore_permissions and xattr_table == null) {
|
||||||
|
path_ret.deinit(alloc);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path_ret.inode.hdr.inode_type == .dir or path_ret.inode.hdr.inode_type == .ext_dir) {
|
||||||
|
try dir_queue.push(alloc, path_ret);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
defer path_ret.deinit(alloc);
|
||||||
|
try path_ret.setMetadata(alloc, io, &id_table, if (xattr_table == null) null else &xattr_table.?, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
var iter = dir_queue.iterator();
|
||||||
|
while (iter.next()) |path_ret| {
|
||||||
|
defer path_ret.deinit(alloc);
|
||||||
|
try path_ret.setMetadata(alloc, io, &id_table, if (xattr_table == null) null else &xattr_table.?, options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Extracts the given inode to the given path. If the inode not a directory, the given path must not exist.
|
||||||
|
/// If the inode is a directory the path must not exist or be a directory.
|
||||||
|
fn extractSinglethreaded(
|
||||||
|
self: Inode,
|
||||||
|
alloc: std.mem.Allocator,
|
||||||
|
io: Io,
|
||||||
|
fil: OffsetFile,
|
||||||
|
super: Archive.Superblock,
|
||||||
|
path: []const u8,
|
||||||
|
options: ExtractionOptions,
|
||||||
|
decomp: *const Decompressor,
|
||||||
|
frag: *FragManager,
|
||||||
|
) !void {
|
||||||
|
var id_table: CachedTable(u16) = .init(alloc, fil, decomp, super.id_start, super.id_count);
|
||||||
|
defer id_table.deinit(io);
|
||||||
|
|
||||||
|
var xattr_table: ?XattrTable = if (super.flags.xattr_never or options.ignore_xattr or !@hasField(std.os, "linux"))
|
||||||
|
null
|
||||||
|
else
|
||||||
|
try .init(alloc, fil, decomp, super.xattr_start);
|
||||||
|
defer if (xattr_table != null) xattr_table.?.deinit(io);
|
||||||
|
|
||||||
|
return self.extractReal(
|
||||||
|
alloc,
|
||||||
|
io,
|
||||||
|
fil,
|
||||||
|
super,
|
||||||
|
decomp,
|
||||||
|
frag,
|
||||||
|
&id_table,
|
||||||
|
if (xattr_table == null) null else &xattr_table.?,
|
||||||
|
path,
|
||||||
|
options,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
fn extractReal(
|
||||||
|
self: Inode,
|
||||||
|
alloc: std.mem.Allocator,
|
||||||
|
io: Io,
|
||||||
|
fil: OffsetFile,
|
||||||
|
super: Archive.Superblock,
|
||||||
|
decomp: *const Decompressor,
|
||||||
|
frag_mgr: *FragManager,
|
||||||
|
id_table: *CachedTable(u16),
|
||||||
|
xattr_table: ?*XattrTable,
|
||||||
|
path: []const u8,
|
||||||
|
options: ExtractionOptions,
|
||||||
|
) !void {
|
||||||
|
switch (self.hdr.inode_type) {
|
||||||
|
.dir, .ext_dir => {
|
||||||
|
try Io.Dir.cwd().createDir(io, path, @enumFromInt(0o777));
|
||||||
|
|
||||||
|
const entries = self.readDirectory(alloc, fil, decomp, super.dir_start) catch |err| switch (err) {
|
||||||
|
Error.NotDirectory, Error.NotExtended, Error.NotRegularFile, Error.NotSymlink => unreachable,
|
||||||
|
else => |e| return e,
|
||||||
|
};
|
||||||
|
defer {
|
||||||
|
for (entries) |e|
|
||||||
|
e.deinit(alloc);
|
||||||
|
alloc.free(entries);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (entries) |e| {
|
||||||
|
const new_path = try std.mem.concat(alloc, u8, &[_][]const u8{ path, "/", e.name });
|
||||||
|
defer alloc.free(new_path);
|
||||||
|
|
||||||
|
var rdr = fil.readerAt(super.inode_start + e.block_start);
|
||||||
|
var meta: MetadataReader = .init(alloc, &rdr, decomp);
|
||||||
|
try meta.interface.discardAll(e.block_offset);
|
||||||
|
|
||||||
|
const new_inode = try read(alloc, &meta.interface, super.block_size);
|
||||||
|
defer new_inode.deinit(alloc);
|
||||||
|
|
||||||
|
try new_inode.extractReal(alloc, io, fil, super, decomp, frag_mgr, id_table, xattr_table, new_path, options);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.file, .ext_file => {
|
||||||
|
var atomic = try Io.Dir.cwd().createFileAtomic(io, path, .{ .make_path = true });
|
||||||
|
defer atomic.deinit(io);
|
||||||
|
|
||||||
|
var rdr: DataReader = switch (self.data) {
|
||||||
|
.file => |f| blk: {
|
||||||
|
var ext: DataReader = try .init(alloc, io, fil, decomp, super.block_size, f.size, f.block_start, f.block_sizes);
|
||||||
|
if (f.frag_idx != 0xFFFFFFFF)
|
||||||
|
ext.addFrag(f.frag_block_offset, try frag_mgr.get(io, f.frag_idx));
|
||||||
|
break :blk ext;
|
||||||
|
},
|
||||||
|
.ext_file => |f| blk: {
|
||||||
|
var ext: DataReader = try .init(alloc, io, fil, decomp, super.block_size, f.size, f.block_start, f.block_sizes);
|
||||||
|
if (f.frag_idx != 0xFFFFFFFF)
|
||||||
|
ext.addFrag(f.frag_block_offset, try frag_mgr.get(io, f.frag_idx));
|
||||||
|
break :blk ext;
|
||||||
|
},
|
||||||
|
else => unreachable,
|
||||||
|
};
|
||||||
|
defer rdr.deinit();
|
||||||
|
|
||||||
|
var buf: [512 * 1024]u8 = undefined;
|
||||||
|
var wrt = atomic.file.writer(io, &buf);
|
||||||
|
|
||||||
|
_ = try rdr.interface.streamRemaining(&wrt.interface);
|
||||||
|
|
||||||
|
try wrt.flush();
|
||||||
|
|
||||||
|
try atomic.link(io);
|
||||||
|
},
|
||||||
|
.symlink, .ext_symlink => try Io.Dir.cwd().symLink(io, self.symlinkTarget() catch unreachable, path, .{}),
|
||||||
|
else => {
|
||||||
|
var mode: u32 = undefined;
|
||||||
|
var dev: u32 = 0;
|
||||||
|
|
||||||
|
const DT = std.posix.DT;
|
||||||
|
|
||||||
|
switch (self.data) {
|
||||||
|
.char_dev => |d| {
|
||||||
|
dev = d.dev;
|
||||||
|
mode = DT.CHR;
|
||||||
|
},
|
||||||
|
.ext_char_dev => |d| {
|
||||||
|
dev = d.dev;
|
||||||
|
mode = DT.CHR;
|
||||||
|
},
|
||||||
|
.block_dev => |d| {
|
||||||
|
dev = d.dev;
|
||||||
|
mode = DT.BLK;
|
||||||
|
},
|
||||||
|
.ext_block_dev => |d| {
|
||||||
|
dev = d.dev;
|
||||||
|
mode = DT.BLK;
|
||||||
|
},
|
||||||
|
.fifo, .ext_fifo => mode = DT.FIFO,
|
||||||
|
.socket, .ext_socket => mode = DT.SOCK,
|
||||||
|
else => unreachable,
|
||||||
|
}
|
||||||
|
|
||||||
|
const sentinel_path = try std.mem.concatWithSentinel(alloc, u8, &[_][]const u8{path}, 0);
|
||||||
|
const res = std.os.linux.mknod(sentinel_path, mode, dev);
|
||||||
|
alloc.free(sentinel_path);
|
||||||
|
if (res != 0)
|
||||||
|
return ExtractError.MknodFailed;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if (options.ignore_permissions and options.ignore_xattr) return;
|
||||||
|
|
||||||
|
var f = try Io.Dir.cwd().openFile(io, path, .{});
|
||||||
|
defer f.close(io);
|
||||||
|
|
||||||
|
if (!options.ignore_permissions) {
|
||||||
|
try f.setPermissions(io, @enumFromInt(self.hdr.permissions));
|
||||||
|
try f.setOwner(io, try id_table.get(io, self.hdr.uid_idx), try id_table.get(io, self.hdr.gid_idx));
|
||||||
|
}
|
||||||
|
if (xattr_table != null) {
|
||||||
|
const idx = self.xattrIndex() catch return;
|
||||||
|
|
||||||
|
const xattrs = try xattr_table.?.get(alloc, io, idx);
|
||||||
|
defer {
|
||||||
|
for (xattrs) |x|
|
||||||
|
x.deinit(alloc);
|
||||||
|
alloc.free(xattrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
const sentinel_path = try std.mem.concatWithSentinel(alloc, u8, &[_][]const u8{path}, 0);
|
||||||
|
defer alloc.free(sentinel_path);
|
||||||
|
for (xattrs) |x| {
|
||||||
|
const xattr_ret = std.os.linux.fsetxattr(f.handle, x.key, x.value.ptr, x.value.len, 0);
|
||||||
|
if (xattr_ret != 0)
|
||||||
|
return ExtractError.CannotSetXattr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+7
-9
@@ -5,8 +5,10 @@ const Writer = std.Io.Writer;
|
|||||||
|
|
||||||
const ExtractionOptions = @This();
|
const ExtractionOptions = @This();
|
||||||
|
|
||||||
// /// The number of threads used for extraction. 0 implies single threaded.
|
/// Extract single-threaded only.
|
||||||
// threads: usize = 1, // As of Zig 0.16 this should no longer be necessary, instead this should be set by the io instance used.
|
/// Though not necessary if using Threaded.single_threaded,
|
||||||
|
/// setting single_threaded is more efficient.
|
||||||
|
single_threaded: bool = false,
|
||||||
/// Don't set the file's owner & permissions after extraction
|
/// Don't set the file's owner & permissions after extraction
|
||||||
ignore_permissions: bool = false,
|
ignore_permissions: bool = false,
|
||||||
/// Don't set xattr values. Currently xattrs are never set anyway.
|
/// Don't set xattr values. Currently xattrs are never set anyway.
|
||||||
@@ -18,16 +20,12 @@ verbose: bool = false,
|
|||||||
/// Where to print verbose log.
|
/// Where to print verbose log.
|
||||||
verbose_writer: ?*Writer = null,
|
verbose_writer: ?*Writer = null,
|
||||||
|
|
||||||
pub const SingleThreadedDefault: ExtractionOptions = .{};
|
pub const default: ExtractionOptions = .{};
|
||||||
pub fn Default() !ExtractionOptions {
|
pub const default_single_threaded: ExtractionOptions = .{ .single_threaded = true };
|
||||||
return .{
|
|
||||||
.threads = try std.Thread.getCpuCount(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
pub fn VerboseDefault(wrt: *Writer) !ExtractionOptions {
|
pub fn VerboseDefault(wrt: *Writer) !ExtractionOptions {
|
||||||
return .{
|
return .{
|
||||||
.verbose = true,
|
.verbose = true,
|
||||||
.verbose_writer = wrt,
|
.verbose_writer = wrt,
|
||||||
.threads = try std.Thread.getCpuCount(),
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,2 +1,6 @@
|
|||||||
pub const Archive = @import("archive.zig");
|
pub const Archive = @import("archive.zig");
|
||||||
pub const ExtractionOptions = @import("options.zig");
|
pub const ExtractionOptions = @import("options.zig");
|
||||||
|
|
||||||
|
test {
|
||||||
|
@import("std").testing.refAllDecls(@This());
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,92 +0,0 @@
|
|||||||
const std = @import("std");
|
|
||||||
const Io = std.Io;
|
|
||||||
const io = std.testing.io;
|
|
||||||
const alloc = std.testing.allocator;
|
|
||||||
const stuff = @import("builtin");
|
|
||||||
|
|
||||||
const Archive = @import("archive.zig");
|
|
||||||
const Superblock = Archive.Superblock;
|
|
||||||
|
|
||||||
const TestArchive = "testing/LinuxPATest.sfs";
|
|
||||||
|
|
||||||
test "Basics" {
|
|
||||||
std.debug.print("Starting test: Basics...\n", .{});
|
|
||||||
|
|
||||||
var fil = try Io.Dir.cwd().openFile(io, TestArchive, .{});
|
|
||||||
defer fil.close(io);
|
|
||||||
var sfs: Archive = try .init(io, fil, 0);
|
|
||||||
defer sfs.deinit(io);
|
|
||||||
try std.testing.expectEqualDeep(sfs.super, LinuxPATestCorrectSuperblock);
|
|
||||||
const root_file = try sfs.root(alloc, io);
|
|
||||||
defer root_file.deinit();
|
|
||||||
}
|
|
||||||
|
|
||||||
const TestFile = "Start.exe";
|
|
||||||
const TestFileExtractLocation = "testing/Start.exe";
|
|
||||||
|
|
||||||
test "ExtractSingleFile" {
|
|
||||||
std.debug.print("Starting test: ExtractSingleFile...\n", .{});
|
|
||||||
|
|
||||||
Io.Dir.cwd().deleteFile(io, TestFileExtractLocation) catch {};
|
|
||||||
var fil = try Io.Dir.cwd().openFile(io, TestArchive, .{});
|
|
||||||
defer fil.close(io);
|
|
||||||
var sfs: Archive = try .init(io, fil, 0);
|
|
||||||
defer sfs.deinit(io);
|
|
||||||
var test_fil = try sfs.open(alloc, io, TestFile);
|
|
||||||
defer test_fil.deinit();
|
|
||||||
try test_fil.extract(alloc, io, TestFileExtractLocation, try .Default());
|
|
||||||
//TODO: validate extracted file.
|
|
||||||
}
|
|
||||||
|
|
||||||
const TestFullExtractLocation = "testing/TestExtract";
|
|
||||||
|
|
||||||
test "ExtractCompleteArchive" {
|
|
||||||
std.debug.print("Starting test: ExtractCompleteArchive...\n", .{});
|
|
||||||
|
|
||||||
Io.Dir.cwd().deleteTree(io, TestFullExtractLocation) catch {};
|
|
||||||
var fil = try Io.Dir.cwd().openFile(io, TestArchive, .{});
|
|
||||||
defer fil.close(io);
|
|
||||||
var sfs: Archive = try .init(io, fil, 0);
|
|
||||||
defer sfs.deinit(io);
|
|
||||||
try sfs.extract(alloc, io, TestFullExtractLocation, try .Default());
|
|
||||||
}
|
|
||||||
|
|
||||||
const LinuxPATestCorrectSuperblock: Superblock = .{
|
|
||||||
.magic = std.mem.readInt(u32, "hsqs", .little),
|
|
||||||
.inode_count = 2974,
|
|
||||||
.mod_time = 1632696724,
|
|
||||||
.block_size = 131072,
|
|
||||||
.frag_count = 264,
|
|
||||||
.compression = .zstd,
|
|
||||||
.block_log = 17,
|
|
||||||
.flags = .{
|
|
||||||
.inode_uncompressed = false,
|
|
||||||
.data_uncompressed = false,
|
|
||||||
.check = false,
|
|
||||||
.frag_uncompressed = false,
|
|
||||||
.fragment_never = false,
|
|
||||||
.fragment_always = false,
|
|
||||||
.duplicates = true,
|
|
||||||
.exportable = true,
|
|
||||||
.xattr_uncompressed = false,
|
|
||||||
.xattr_never = false,
|
|
||||||
.compression_options = false,
|
|
||||||
.ids_uncompressed = false,
|
|
||||||
._ = 0,
|
|
||||||
},
|
|
||||||
.id_count = 1,
|
|
||||||
.ver_maj = 4,
|
|
||||||
.ver_min = 0,
|
|
||||||
.root_ref = .{
|
|
||||||
.block_offset = 1363,
|
|
||||||
.block_start = 29237,
|
|
||||||
._ = 0,
|
|
||||||
},
|
|
||||||
.size = 106841744,
|
|
||||||
.id_start = 106841632,
|
|
||||||
.xattr_start = 106841720,
|
|
||||||
.inode_start = 106778274,
|
|
||||||
.dir_start = 106807998,
|
|
||||||
.frag_start = 106837747,
|
|
||||||
.export_start = 106841602,
|
|
||||||
};
|
|
||||||
@@ -49,6 +49,23 @@ fn numBlocks(self: DataExtractor) usize {
|
|||||||
return num;
|
return num;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Starts extracting the data using the given group to spawn async tasks.
|
||||||
|
pub fn extractConcurrent(self: DataExtractor, alloc: std.mem.Allocator, io: Io, fil: Io.File) (Error || Io.ConcurrentError)!void {
|
||||||
|
var group: Io.Group = .init;
|
||||||
|
defer group.cancel(io);
|
||||||
|
var err: ?Error = null;
|
||||||
|
|
||||||
|
var read_offset: u64 = self.start;
|
||||||
|
for (0..self.blocks.len) |idx| {
|
||||||
|
try group.concurrent(io, blockThread, .{ self, alloc, io, fil, read_offset, idx, &err });
|
||||||
|
read_offset += self.blocks[idx].size;
|
||||||
|
}
|
||||||
|
if (self.frag_block != null)
|
||||||
|
try group.concurrent(io, fragThread, .{ self, io, fil, &err });
|
||||||
|
|
||||||
|
group.await(io) catch |cancel| return err orelse cancel;
|
||||||
|
}
|
||||||
|
|
||||||
/// Starts extracting the data using the given group to spawn async tasks.
|
/// Starts extracting the data using the given group to spawn async tasks.
|
||||||
pub fn extractAsync(self: DataExtractor, alloc: std.mem.Allocator, io: Io, fil: Io.File) Error!void {
|
pub fn extractAsync(self: DataExtractor, alloc: std.mem.Allocator, io: Io, fil: Io.File) Error!void {
|
||||||
var group: Io.Group = .init;
|
var group: Io.Group = .init;
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ pub fn init(io: Io, fil: File, archive_size: u64, init_offset: u64) !OffsetFile
|
|||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
pub fn deinit(self: @This(), io: Io) void {
|
pub fn deinit(self: *OffsetFile, io: Io) void {
|
||||||
self.map.destroy(io);
|
self.map.destroy(io);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user