Bug fixes
This commit is contained in:
+22
-1
@@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
"build": {
|
"build": {
|
||||||
"command": "zig",
|
"command": "zig",
|
||||||
"args": ["build", "-Duse_c_libs=true", "-Ddebug=true"],
|
"args": ["build", "-Ddebug=true"],
|
||||||
},
|
},
|
||||||
|
|
||||||
"program": "zig-out/bin/unsquashfs",
|
"program": "zig-out/bin/unsquashfs",
|
||||||
@@ -22,4 +22,25 @@
|
|||||||
"testing/LinuxPATest.sfs",
|
"testing/LinuxPATest.sfs",
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"label": "Build & Run Single-Threaded",
|
||||||
|
|
||||||
|
"adapter": "CodeLLDB",
|
||||||
|
"request": "launch",
|
||||||
|
|
||||||
|
"build": {
|
||||||
|
"command": "zig",
|
||||||
|
"args": ["build", "-Ddebug=true"],
|
||||||
|
},
|
||||||
|
|
||||||
|
"program": "zig-out/bin/unsquashfs",
|
||||||
|
"args": [
|
||||||
|
"--force",
|
||||||
|
"-p",
|
||||||
|
"1",
|
||||||
|
"-d",
|
||||||
|
"testing/TestExtractUnsquashfs",
|
||||||
|
"testing/LinuxPATest.sfs",
|
||||||
|
],
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -12,10 +12,10 @@ pub fn build(b: *std.Build) !void {
|
|||||||
|
|
||||||
const version: std.SemanticVersion = try .parse(version_string_option);
|
const version: std.SemanticVersion = try .parse(version_string_option);
|
||||||
|
|
||||||
const build_options = b.addOptions();
|
const options = b.addOptions();
|
||||||
build_options.addOption(bool, "use_zig_decomp", use_zig_decomp);
|
options.addOption(bool, "use_zig_decomp", use_zig_decomp);
|
||||||
build_options.addOption(bool, "allow_lzo", allow_lzo);
|
options.addOption(bool, "allow_lzo", allow_lzo);
|
||||||
build_options.addOption(std.SemanticVersion, "version", version);
|
options.addOption(std.SemanticVersion, "version", version);
|
||||||
|
|
||||||
const c = b.addTranslateC(.{
|
const c = b.addTranslateC(.{
|
||||||
.optimize = optimize,
|
.optimize = optimize,
|
||||||
@@ -44,7 +44,7 @@ pub fn build(b: *std.Build) !void {
|
|||||||
.valgrind = debug,
|
.valgrind = debug,
|
||||||
.imports = &.{
|
.imports = &.{
|
||||||
.{ .name = "c", .module = c.createModule() },
|
.{ .name = "c", .module = c.createModule() },
|
||||||
.{ .name = "build_options", .module = build_options.createModule() },
|
.{ .name = "build_options", .module = options.createModule() },
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
@@ -69,6 +69,9 @@ pub fn build(b: *std.Build) !void {
|
|||||||
|
|
||||||
b.installArtifact(lib);
|
b.installArtifact(lib);
|
||||||
|
|
||||||
|
const unsquashfs_options = b.addOptions();
|
||||||
|
unsquashfs_options.addOption(std.SemanticVersion, "version", version);
|
||||||
|
|
||||||
const exe = b.addExecutable(.{
|
const exe = b.addExecutable(.{
|
||||||
.name = "unsquashfs",
|
.name = "unsquashfs",
|
||||||
.use_llvm = debug,
|
.use_llvm = debug,
|
||||||
@@ -80,11 +83,13 @@ pub fn build(b: *std.Build) !void {
|
|||||||
.valgrind = debug,
|
.valgrind = debug,
|
||||||
.imports = &.{
|
.imports = &.{
|
||||||
.{ .name = "squashfs", .module = lib.root_module },
|
.{ .name = "squashfs", .module = lib.root_module },
|
||||||
.{ .name = "build_options", .module = build_options.createModule() },
|
.{ .name = "build", .module = unsquashfs_options.createModule() },
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
b.installArtifact(exe);
|
||||||
|
|
||||||
const mod_tests = b.addTest(.{
|
const mod_tests = b.addTest(.{
|
||||||
.root_module = lib.root_module,
|
.root_module = lib.root_module,
|
||||||
.use_llvm = debug,
|
.use_llvm = debug,
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ const std = @import("std");
|
|||||||
const Io = std.Io;
|
const Io = std.Io;
|
||||||
const Writer = Io.Writer;
|
const Writer = Io.Writer;
|
||||||
const builtin = @import("builtin");
|
const builtin = @import("builtin");
|
||||||
const build = @import("build_options");
|
const build = @import("build");
|
||||||
|
|
||||||
const squashfs = @import("squashfs");
|
const squashfs = @import("squashfs");
|
||||||
|
|
||||||
@@ -41,7 +41,7 @@ var force: bool = false;
|
|||||||
|
|
||||||
pub fn main(init: std.process.Init) !void {
|
pub fn main(init: std.process.Init) !void {
|
||||||
const io = init.io;
|
const io = init.io;
|
||||||
const alloc = init.alloc;
|
const alloc = init.gpa;
|
||||||
|
|
||||||
var stdout = Io.File.stdout();
|
var stdout = Io.File.stdout();
|
||||||
var out = stdout.writer(io, &[0]u8{});
|
var out = stdout.writer(io, &[0]u8{});
|
||||||
@@ -54,23 +54,23 @@ pub fn main(init: std.process.Init) !void {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var fil = try Io.Dir.cwd().openFile(io, archive, .{}); //TODO: Handle error gracefully.
|
var fil = try Io.Dir.cwd().openFile(io, archive, .{}); //TODO: Handle error gracefully.
|
||||||
defer fil.close();
|
defer fil.close(io);
|
||||||
var arc: squashfs.Archive = try .init(io, fil, offset); //TODO: Update when memory size matters. //TODO: Handle error gracefully.
|
var arc: squashfs.Archive = try .init(io, fil, offset); //TODO: Update when memory size matters. //TODO: Handle error gracefully.
|
||||||
defer arc.deinit();
|
defer arc.deinit(io);
|
||||||
const options: squashfs.ExtractionOptions = .{
|
const options: squashfs.ExtractionOptions = .{
|
||||||
.single_threaded = threads == 1,
|
.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,
|
||||||
.ignore_permissions = ignore_permissions,
|
.ignore_permissions = ignore_permissions,
|
||||||
};
|
};
|
||||||
if (force)
|
if (force)
|
||||||
try std.fs.cwd().deleteTree(ext_loc);
|
try Io.Dir.cwd().deleteTree(io, ext_loc);
|
||||||
if (threads > 1) {
|
if (threads > 1) {
|
||||||
var limited_io = Io.Threaded.init(alloc, .{
|
var limited_io = Io.Threaded.init(alloc, .{
|
||||||
.argv0 = .init(init.minimal.args),
|
.argv0 = .init(init.minimal.args),
|
||||||
.async_limit = threads,
|
.async_limit = @enumFromInt(threads),
|
||||||
.concurrent_limit = threads,
|
.concurrent_limit = @enumFromInt(threads),
|
||||||
.environ = init.minimal.environ,
|
.environ = init.minimal.environ,
|
||||||
});
|
});
|
||||||
try arc.extract(alloc, limited_io.io(), ext_loc, options); //TODO: Handle error gracefully.
|
try arc.extract(alloc, limited_io.io(), ext_loc, options); //TODO: Handle error gracefully.
|
||||||
|
|||||||
+3
-1
@@ -48,7 +48,9 @@ pub fn lz4(_: std.mem.Allocator, in: []u8, out: []u8) Error!usize {
|
|||||||
}
|
}
|
||||||
pub fn zstd(_: std.mem.Allocator, in: []u8, out: []u8) Error!usize {
|
pub fn zstd(_: std.mem.Allocator, in: []u8, out: []u8) Error!usize {
|
||||||
const res = c.ZSTD_decompress(out.ptr, out.len, in.ptr, in.len);
|
const res = c.ZSTD_decompress(out.ptr, out.len, in.ptr, in.len);
|
||||||
if (c.ZSTD_isError(res) != 0)
|
if (c.ZSTD_isError(res) != 0) {
|
||||||
|
std.debug.print("decompression failed: {s}\n", .{c.ZSTD_getErrorName(res)});
|
||||||
return Error.DecompressionFailed;
|
return Error.DecompressionFailed;
|
||||||
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,7 +76,8 @@ fn blockThread(self: Extractor, alloc: std.mem.Allocator, io: Io, map_data: []u8
|
|||||||
self.size % self.block_size
|
self.size % self.block_size
|
||||||
else
|
else
|
||||||
self.block_size;
|
self.block_size;
|
||||||
const offset = self.block_size * block_idx;
|
|
||||||
|
const offset = block_idx * self.block_size;
|
||||||
|
|
||||||
const block = self.blocks[block_idx];
|
const block = self.blocks[block_idx];
|
||||||
|
|
||||||
@@ -92,17 +93,22 @@ fn blockThread(self: Extractor, alloc: std.mem.Allocator, io: Io, map_data: []u8
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std.debug.print("offset: {} start: {} block: {any}\n", .{ read_offset, self.start, block });
|
||||||
|
|
||||||
if (self.cache != null) {
|
if (self.cache != null) {
|
||||||
const decomp_block = self.cache.?.get(io, read_offset, block.size) catch |inner_err| {
|
const decomp_block = self.cache.?.get(io, read_offset, block.size) catch |inner_err| {
|
||||||
|
std.debug.print("cache extractor err: {}\n", .{inner_err});
|
||||||
switch (inner_err) {
|
switch (inner_err) {
|
||||||
error.Canceled => return error.Canceled,
|
error.Canceled => return error.Canceled,
|
||||||
else => |e| err.* = e,
|
else => |e| err.* = e,
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
std.debug.print("cached block size: {} should be {}\n", .{ decomp_block.len, size });
|
||||||
@memcpy(map_data[offset..][0..size], decomp_block[0..size]);
|
@memcpy(map_data[offset..][0..size], decomp_block[0..size]);
|
||||||
} else {
|
} else {
|
||||||
_ = self.decomp(alloc, data, map_data[offset..][0..size]) catch |inner_err| {
|
_ = self.decomp(alloc, data, map_data[offset..][0..size]) catch |inner_err| {
|
||||||
|
std.debug.print("data decomp err: {}\n", .{inner_err});
|
||||||
err.* = inner_err;
|
err.* = inner_err;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
+58
-12
@@ -2,6 +2,7 @@ const std = @import("std");
|
|||||||
const Io = std.Io;
|
const Io = std.Io;
|
||||||
|
|
||||||
const DataExtractor = @import("data/extractor.zig");
|
const DataExtractor = @import("data/extractor.zig");
|
||||||
|
const DataReader = @import("data/reader.zig");
|
||||||
const Decomp = @import("decomp.zig");
|
const Decomp = @import("decomp.zig");
|
||||||
const Directory = @import("directory.zig");
|
const Directory = @import("directory.zig");
|
||||||
const ExtractionOptions = @import("options.zig");
|
const ExtractionOptions = @import("options.zig");
|
||||||
@@ -83,7 +84,7 @@ fn finishLoop(alloc: std.mem.Allocator, io: Io, sel: *Io.Select(ReturnUnion), id
|
|||||||
var dirs: std.PriorityDequeue(PathReturn, void, dirOrder) = .empty;
|
var dirs: std.PriorityDequeue(PathReturn, void, dirOrder) = .empty;
|
||||||
defer dirs.deinit(alloc);
|
defer dirs.deinit(alloc);
|
||||||
errdefer while (dirs.popMax()) |d|
|
errdefer while (dirs.popMax()) |d|
|
||||||
alloc.free(d.path);
|
if (d.hdr.num != start_num) alloc.free(d.path);
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
const value: ReturnUnion = try sel.await();
|
const value: ReturnUnion = try sel.await();
|
||||||
@@ -178,7 +179,7 @@ fn extractDir(
|
|||||||
origin: bool,
|
origin: bool,
|
||||||
) Error!PathReturn {
|
) Error!PathReturn {
|
||||||
defer if (!origin) inode.deinit(alloc);
|
defer if (!origin) inode.deinit(alloc);
|
||||||
errdefer alloc.free(path);
|
errdefer if (!origin) alloc.free(path);
|
||||||
|
|
||||||
var ret: PathReturn = .{
|
var ret: PathReturn = .{
|
||||||
.hdr = inode.hdr,
|
.hdr = inode.hdr,
|
||||||
@@ -189,7 +190,7 @@ fn extractDir(
|
|||||||
|
|
||||||
var dir: Directory = switch (inode.data) {
|
var dir: Directory = switch (inode.data) {
|
||||||
.dir => |d| blk: {
|
.dir => |d| blk: {
|
||||||
var meta: MetadataReader = .init(alloc, data, decomp, d.block_start);
|
var meta: MetadataReader = .init(alloc, data, decomp, d.block_start + super.dir_start);
|
||||||
try meta.interface.discardAll(d.block_offset);
|
try meta.interface.discardAll(d.block_offset);
|
||||||
|
|
||||||
break :blk try Directory.init(alloc, &meta.interface, d.size);
|
break :blk try Directory.init(alloc, &meta.interface, d.size);
|
||||||
@@ -197,7 +198,7 @@ fn extractDir(
|
|||||||
.ext_dir => |d| blk: {
|
.ext_dir => |d| blk: {
|
||||||
if (d.xattr_idx != 0xFFFFFFFF) ret.xattr_idx = d.xattr_idx;
|
if (d.xattr_idx != 0xFFFFFFFF) ret.xattr_idx = d.xattr_idx;
|
||||||
|
|
||||||
var meta: MetadataReader = .init(alloc, data, decomp, d.block_start);
|
var meta: MetadataReader = .init(alloc, data, decomp, d.block_start + super.dir_start);
|
||||||
try meta.interface.discardAll(d.block_offset);
|
try meta.interface.discardAll(d.block_offset);
|
||||||
|
|
||||||
break :blk try Directory.init(alloc, &meta.interface, d.size);
|
break :blk try Directory.init(alloc, &meta.interface, d.size);
|
||||||
@@ -237,7 +238,7 @@ fn extractFile(
|
|||||||
origin: bool,
|
origin: bool,
|
||||||
) Error!PathReturn {
|
) Error!PathReturn {
|
||||||
defer if (!origin) inode.deinit(alloc);
|
defer if (!origin) inode.deinit(alloc);
|
||||||
errdefer alloc.free(path);
|
errdefer if (!origin) alloc.free(path);
|
||||||
|
|
||||||
try io.checkCancel();
|
try io.checkCancel();
|
||||||
|
|
||||||
@@ -246,10 +247,53 @@ fn extractFile(
|
|||||||
.path = path,
|
.path = path,
|
||||||
};
|
};
|
||||||
|
|
||||||
var ext: DataExtractor = switch (inode.data) {
|
// var ext: DataExtractor = switch (inode.data) {
|
||||||
|
// .file => |f| blk: {
|
||||||
|
// var rdr: DataExtractor = .init(data, decomp, block_size, f.blocks, f.size, f.block_start);
|
||||||
|
// rdr.addCache(cache);
|
||||||
|
|
||||||
|
// if (f.frag_idx == 0xFFFFFFFF) break :blk rdr;
|
||||||
|
|
||||||
|
// const entry: Lookup.FragEntry = try frag_table.get(io, f.frag_idx);
|
||||||
|
// if (entry.size.uncompressed) {
|
||||||
|
// rdr.addFrag(data[entry.block_start..][0..entry.size.size], f.frag_offset);
|
||||||
|
// } else {
|
||||||
|
// rdr.addFrag(try cache.get(io, entry.block_start, entry.size.size), f.frag_offset);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// break :blk rdr;
|
||||||
|
// },
|
||||||
|
// .ext_file => |f| blk: {
|
||||||
|
// if (f.xattr_idx != 0xFFFFFFFF) ret.xattr_idx = f.xattr_idx;
|
||||||
|
|
||||||
|
// var rdr: DataExtractor = .init(data, decomp, block_size, f.blocks, f.size, f.block_start);
|
||||||
|
// rdr.addCache(cache);
|
||||||
|
|
||||||
|
// if (f.frag_idx == 0xFFFFFFFF) break :blk rdr;
|
||||||
|
|
||||||
|
// const entry: Lookup.FragEntry = try frag_table.get(io, f.frag_idx);
|
||||||
|
// if (entry.size.uncompressed) {
|
||||||
|
// rdr.addFrag(data[entry.block_start..][0..entry.size.size], f.frag_offset);
|
||||||
|
// } else {
|
||||||
|
// rdr.addFrag(try cache.get(io, entry.block_start, entry.size.size), f.frag_offset);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// break :blk rdr;
|
||||||
|
// },
|
||||||
|
// else => unreachable,
|
||||||
|
// };
|
||||||
|
|
||||||
|
// var atomic = try Io.Dir.cwd().createFileAtomic(io, path, .{});
|
||||||
|
// defer atomic.deinit(io);
|
||||||
|
|
||||||
|
// try ext.extractAsync(alloc, io, atomic.file);
|
||||||
|
|
||||||
|
// try atomic.link(io);
|
||||||
|
|
||||||
|
var rdr: DataReader = switch (inode.data) {
|
||||||
.file => |f| blk: {
|
.file => |f| blk: {
|
||||||
var rdr: DataExtractor = .init(data, decomp, block_size, f.blocks, f.size, f.block_start);
|
var rdr: DataReader = .init(alloc, data, decomp, block_size, f.blocks, f.size, f.block_start);
|
||||||
rdr.addCache(cache);
|
rdr.addCache(io, cache);
|
||||||
|
|
||||||
if (f.frag_idx == 0xFFFFFFFF) break :blk rdr;
|
if (f.frag_idx == 0xFFFFFFFF) break :blk rdr;
|
||||||
|
|
||||||
@@ -265,8 +309,8 @@ fn extractFile(
|
|||||||
.ext_file => |f| blk: {
|
.ext_file => |f| blk: {
|
||||||
if (f.xattr_idx != 0xFFFFFFFF) ret.xattr_idx = f.xattr_idx;
|
if (f.xattr_idx != 0xFFFFFFFF) ret.xattr_idx = f.xattr_idx;
|
||||||
|
|
||||||
var rdr: DataExtractor = .init(data, decomp, block_size, f.blocks, f.size, f.block_start);
|
var rdr: DataReader = .init(alloc, data, decomp, block_size, f.blocks, f.size, f.block_start);
|
||||||
rdr.addCache(cache);
|
rdr.addCache(io, cache);
|
||||||
|
|
||||||
if (f.frag_idx == 0xFFFFFFFF) break :blk rdr;
|
if (f.frag_idx == 0xFFFFFFFF) break :blk rdr;
|
||||||
|
|
||||||
@@ -285,7 +329,9 @@ fn extractFile(
|
|||||||
var atomic = try Io.Dir.cwd().createFileAtomic(io, path, .{});
|
var atomic = try Io.Dir.cwd().createFileAtomic(io, path, .{});
|
||||||
defer atomic.deinit(io);
|
defer atomic.deinit(io);
|
||||||
|
|
||||||
try ext.extractAsync(alloc, io, atomic.file);
|
var writer = atomic.file.writer(io, &[0]u8{});
|
||||||
|
_ = try rdr.interface.streamRemaining(&writer.interface);
|
||||||
|
try writer.flush();
|
||||||
|
|
||||||
try atomic.link(io);
|
try atomic.link(io);
|
||||||
|
|
||||||
@@ -372,7 +418,7 @@ const ReturnUnion = union(enum) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const Error = error{MknodError} || Decomp.Error || Directory.Error || DataExtractor.Error || Io.Dir.CreateDirPathError ||
|
const Error = error{MknodError} || Decomp.Error || Directory.Error || DataExtractor.Error || Io.Dir.CreateDirPathError ||
|
||||||
Io.Dir.SymLinkError || Io.File.Atomic.LinkError;
|
Io.Dir.SymLinkError || Io.File.Atomic.LinkError || Io.Reader.StreamRemainingError;
|
||||||
|
|
||||||
const PathReturn = struct {
|
const PathReturn = struct {
|
||||||
hdr: Inode.Header,
|
hdr: Inode.Header,
|
||||||
|
|||||||
+101
-60
@@ -36,24 +36,49 @@ pub fn extract(
|
|||||||
var xattr_table: XattrTable = .init(alloc, data, decomp, super.xattr_start);
|
var xattr_table: XattrTable = .init(alloc, data, decomp, super.xattr_start);
|
||||||
defer xattr_table.deinit();
|
defer xattr_table.deinit();
|
||||||
|
|
||||||
return extractReal(
|
return switch (inode.hdr.type) {
|
||||||
alloc,
|
.file, .ext_file => try extractFile(alloc, io, super, data, decomp, &cache, &frag_table, &id_table, &xattr_table, inode, path, options),
|
||||||
io,
|
.dir, .ext_dir => try extractDir(alloc, io, super, data, decomp, &cache, &frag_table, &id_table, &xattr_table, inode, path, options),
|
||||||
super,
|
.symlink, .ext_symlink => try extractSymlink(io, inode, path),
|
||||||
data,
|
else => try extractNod(alloc, io, &id_table, &xattr_table, inode, path, options),
|
||||||
decomp,
|
};
|
||||||
&cache,
|
|
||||||
&frag_table,
|
|
||||||
&id_table,
|
|
||||||
&xattr_table,
|
|
||||||
inode,
|
|
||||||
path,
|
|
||||||
options,
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn extractReal(
|
fn setMetadata(
|
||||||
|
alloc: std.mem.Allocator,
|
||||||
|
io: Io,
|
||||||
|
id_table: *Lookup.Table(u16),
|
||||||
|
xattr_table: *XattrTable,
|
||||||
|
inode: Inode,
|
||||||
|
path: []const u8,
|
||||||
|
options: ExtractionOptions,
|
||||||
|
xattr_idx: ?u32,
|
||||||
|
) !void {
|
||||||
|
if (options.ignore_permissions and (options.ignore_xattr or xattr_idx == null)) return;
|
||||||
|
|
||||||
|
var fil: Io.File = try Io.Dir.cwd().openFile(io, path, .{});
|
||||||
|
defer fil.close(io);
|
||||||
|
|
||||||
|
if (!options.ignore_permissions) {
|
||||||
|
try fil.setTimestamps(io, .{
|
||||||
|
.modify_timestamp = .init(Io.Timestamp.fromNanoseconds(@as(i96, @intCast(inode.hdr.mod_time)) * std.time.ns_per_s)),
|
||||||
|
});
|
||||||
|
try fil.setPermissions(io, @enumFromInt(inode.hdr.permissions));
|
||||||
|
try fil.setOwner(io, try id_table.get(io, inode.hdr.uid_idx), try id_table.get(io, inode.hdr.gid_idx));
|
||||||
|
}
|
||||||
|
if (!options.ignore_xattr and xattr_idx != null) {
|
||||||
|
const xattr = try xattr_table.get(alloc, io, xattr_idx.?);
|
||||||
|
defer xattr.deinit(alloc);
|
||||||
|
|
||||||
|
for (xattr.kvs) |kv| {
|
||||||
|
const res = std.os.linux.fsetxattr(fil.handle, kv.key, kv.value.ptr, kv.value.len, 0);
|
||||||
|
if (res != 0)
|
||||||
|
return error.SetXattrError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extractDir(
|
||||||
alloc: std.mem.Allocator,
|
alloc: std.mem.Allocator,
|
||||||
io: Io,
|
io: Io,
|
||||||
super: Superblock,
|
super: Superblock,
|
||||||
@@ -66,22 +91,17 @@ pub fn extractReal(
|
|||||||
inode: Inode,
|
inode: Inode,
|
||||||
path: []const u8,
|
path: []const u8,
|
||||||
options: ExtractionOptions,
|
options: ExtractionOptions,
|
||||||
origin: bool,
|
|
||||||
) !void {
|
) !void {
|
||||||
defer if (!origin) {
|
std.debug.print("starting extract dir: {s}\n", .{path});
|
||||||
alloc.free(path);
|
defer std.debug.print("end extract dir: {s}\n", .{path});
|
||||||
inode.deinit(alloc);
|
|
||||||
};
|
|
||||||
|
|
||||||
var xattr_idx: ?u32 = null;
|
var xattr_idx: ?u32 = null;
|
||||||
|
|
||||||
switch (inode.data) {
|
|
||||||
.dir, .ext_dir => {
|
|
||||||
try Io.Dir.cwd().createDirPath(io, path);
|
try Io.Dir.cwd().createDirPath(io, path);
|
||||||
|
|
||||||
var dir: Directory = switch (inode.data) {
|
var dir: Directory = switch (inode.data) {
|
||||||
.dir => |d| blk: {
|
.dir => |d| blk: {
|
||||||
var meta: MetadaReader = .init(alloc, data, decomp, d.block_start);
|
var meta: MetadaReader = .init(alloc, data, decomp, d.block_start + super.dir_start);
|
||||||
try meta.interface.discardAll(d.block_offset);
|
try meta.interface.discardAll(d.block_offset);
|
||||||
|
|
||||||
break :blk try Directory.init(alloc, &meta.interface, d.size);
|
break :blk try Directory.init(alloc, &meta.interface, d.size);
|
||||||
@@ -89,7 +109,7 @@ pub fn extractReal(
|
|||||||
.ext_dir => |d| blk: {
|
.ext_dir => |d| blk: {
|
||||||
if (d.xattr_idx != 0xFFFFFFFF) xattr_idx = d.xattr_idx;
|
if (d.xattr_idx != 0xFFFFFFFF) xattr_idx = d.xattr_idx;
|
||||||
|
|
||||||
var meta: MetadaReader = .init(alloc, data, decomp, d.block_start);
|
var meta: MetadaReader = .init(alloc, data, decomp, d.block_start + super.dir_start);
|
||||||
try meta.interface.discardAll(d.block_offset);
|
try meta.interface.discardAll(d.block_offset);
|
||||||
|
|
||||||
break :blk try Directory.init(alloc, &meta.interface, d.size);
|
break :blk try Directory.init(alloc, &meta.interface, d.size);
|
||||||
@@ -100,16 +120,39 @@ pub fn extractReal(
|
|||||||
|
|
||||||
for (dir.entries) |entry| {
|
for (dir.entries) |entry| {
|
||||||
var new_inode: Inode = try .initEntry(alloc, data, decomp, super.inode_start, super.block_size, entry);
|
var new_inode: Inode = try .initEntry(alloc, data, decomp, super.inode_start, super.block_size, entry);
|
||||||
|
defer new_inode.deinit(alloc);
|
||||||
|
|
||||||
const new_path = std.mem.concat(alloc, u8, &.{ path, "/", entry.name }) catch |err| {
|
const new_path = try std.mem.concat(alloc, u8, &.{ path, "/", entry.name });
|
||||||
new_inode.deinit(alloc);
|
defer alloc.free(new_path);
|
||||||
return err;
|
|
||||||
};
|
|
||||||
|
|
||||||
try extractReal(alloc, io, super, data, decomp, cache, frag_table, id_table, xattr_table, new_inode, new_path, options, false);
|
switch (entry.type) {
|
||||||
|
.file => try extractFile(alloc, io, super, data, decomp, cache, frag_table, id_table, xattr_table, new_inode, new_path, options),
|
||||||
|
.dir => try extractDir(alloc, io, super, data, decomp, cache, frag_table, id_table, xattr_table, new_inode, new_path, options),
|
||||||
|
.symlink => try extractSymlink(io, new_inode, new_path),
|
||||||
|
else => try extractNod(alloc, io, id_table, xattr_table, new_inode, new_path, options),
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
.file, .ext_file => {
|
|
||||||
|
try setMetadata(alloc, io, id_table, xattr_table, inode, path, options, xattr_idx);
|
||||||
|
}
|
||||||
|
fn extractFile(
|
||||||
|
alloc: std.mem.Allocator,
|
||||||
|
io: Io,
|
||||||
|
super: Superblock,
|
||||||
|
data: []u8,
|
||||||
|
decomp: Decomp.Fn,
|
||||||
|
cache: *Cache,
|
||||||
|
frag_table: *Lookup.Table(Lookup.FragEntry),
|
||||||
|
id_table: *Lookup.Table(u16),
|
||||||
|
xattr_table: *XattrTable,
|
||||||
|
inode: Inode,
|
||||||
|
path: []const u8,
|
||||||
|
options: ExtractionOptions,
|
||||||
|
) !void {
|
||||||
|
std.debug.print("starting extract file: {s}\n", .{path});
|
||||||
|
defer std.debug.print("end extract file: {s}\n", .{path});
|
||||||
|
var xattr_idx: ?u32 = null;
|
||||||
|
|
||||||
var rdr: DataReader = switch (inode.data) {
|
var rdr: DataReader = switch (inode.data) {
|
||||||
.file => |f| blk: {
|
.file => |f| blk: {
|
||||||
var rdr: DataReader = .init(alloc, data, decomp, super.block_size, f.blocks, f.size, f.block_start);
|
var rdr: DataReader = .init(alloc, data, decomp, super.block_size, f.blocks, f.size, f.block_start);
|
||||||
@@ -154,13 +197,34 @@ pub fn extractReal(
|
|||||||
try writer.flush();
|
try writer.flush();
|
||||||
|
|
||||||
try atomic.link(io);
|
try atomic.link(io);
|
||||||
},
|
|
||||||
.symlink => |s| return Io.Dir.cwd().symLink(io, s.target, path, .{}),
|
try setMetadata(alloc, io, id_table, xattr_table, inode, path, options, xattr_idx);
|
||||||
.ext_symlink => |s| return Io.Dir.cwd().symLink(io, s.target, path, .{}),
|
}
|
||||||
else => {
|
fn extractSymlink(io: Io, inode: Inode, path: []const u8) !void {
|
||||||
|
std.debug.print("starting extract symlink: {s}\n", .{path});
|
||||||
|
defer std.debug.print("end extract symlink: {s}\n", .{path});
|
||||||
|
return switch (inode.data) {
|
||||||
|
.symlink => |s| Io.Dir.cwd().symLink(io, s.target, path, .{}),
|
||||||
|
.ext_symlink => |s| Io.Dir.cwd().symLink(io, s.target, path, .{}),
|
||||||
|
else => unreachable,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
fn extractNod(
|
||||||
|
alloc: std.mem.Allocator,
|
||||||
|
io: Io,
|
||||||
|
id_table: *Lookup.Table(u16),
|
||||||
|
xattr_table: *XattrTable,
|
||||||
|
inode: Inode,
|
||||||
|
path: []const u8,
|
||||||
|
options: ExtractionOptions,
|
||||||
|
) !void {
|
||||||
|
std.debug.print("starting extract nod: {s}\n", .{path});
|
||||||
|
defer std.debug.print("end extract nod: {s}\n", .{path});
|
||||||
var dev: u32 = 0;
|
var dev: u32 = 0;
|
||||||
var mode: u32 = undefined;
|
var mode: u32 = undefined;
|
||||||
|
|
||||||
|
var xattr_idx: ?u32 = null;
|
||||||
|
|
||||||
const DT = std.posix.DT;
|
const DT = std.posix.DT;
|
||||||
|
|
||||||
switch (inode.data) {
|
switch (inode.data) {
|
||||||
@@ -205,29 +269,6 @@ pub fn extractReal(
|
|||||||
const res = std.os.linux.mknod(sentinel_path, mode, dev);
|
const res = std.os.linux.mknod(sentinel_path, mode, dev);
|
||||||
if (res != 0)
|
if (res != 0)
|
||||||
return error.MknodError;
|
return error.MknodError;
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.ignore_permissions and (options.ignore_xattr or xattr_idx == null)) return;
|
try setMetadata(alloc, io, id_table, xattr_table, inode, path, options, xattr_idx);
|
||||||
|
|
||||||
var fil: Io.File = try Io.Dir.cwd().openFile(io, path, .{});
|
|
||||||
defer fil.close(io);
|
|
||||||
|
|
||||||
if (!options.ignore_permissions) {
|
|
||||||
try fil.setTimestamps(io, .{
|
|
||||||
.modify_timestamp = .init(Io.Timestamp.fromNanoseconds(@as(i96, @intCast(inode.hdr.mod_time)) * std.time.ns_per_s)),
|
|
||||||
});
|
|
||||||
try fil.setPermissions(io, @enumFromInt(inode.hdr.permissions));
|
|
||||||
try fil.setOwner(io, try id_table.get(io, inode.hdr.uid_idx), try id_table.get(io, inode.hdr.gid_idx));
|
|
||||||
}
|
|
||||||
if (!options.ignore_xattr and xattr_idx != null) {
|
|
||||||
const xattr = try xattr_table.get(alloc, io, xattr_idx.?);
|
|
||||||
defer xattr.deinit(alloc);
|
|
||||||
|
|
||||||
for (xattr.kvs) |kv| {
|
|
||||||
const res = std.os.linux.fsetxattr(fil.handle, kv.key, kv.value.ptr, kv.value.len, 0);
|
|
||||||
if (res != 0)
|
|
||||||
return error.SetXattrError;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -166,6 +166,7 @@ pub const File = struct {
|
|||||||
blocks_num += 1;
|
blocks_num += 1;
|
||||||
|
|
||||||
const blocks = try alloc.alloc(DataBlock, blocks_num);
|
const blocks = try alloc.alloc(DataBlock, blocks_num);
|
||||||
|
errdefer alloc.free(blocks);
|
||||||
try rdr.readSliceEndian(DataBlock, blocks, .little);
|
try rdr.readSliceEndian(DataBlock, blocks, .little);
|
||||||
|
|
||||||
return .{
|
return .{
|
||||||
@@ -199,6 +200,7 @@ pub const ExtFile = struct {
|
|||||||
blocks_num += 1;
|
blocks_num += 1;
|
||||||
|
|
||||||
const blocks = try alloc.alloc(DataBlock, blocks_num);
|
const blocks = try alloc.alloc(DataBlock, blocks_num);
|
||||||
|
errdefer alloc.free(blocks);
|
||||||
try rdr.readSliceEndian(DataBlock, blocks, .little);
|
try rdr.readSliceEndian(DataBlock, blocks, .little);
|
||||||
|
|
||||||
return .{
|
return .{
|
||||||
@@ -224,6 +226,7 @@ pub const Symlink = struct {
|
|||||||
const target_size = std.mem.readInt(u32, data[4..], .little);
|
const target_size = std.mem.readInt(u32, data[4..], .little);
|
||||||
|
|
||||||
const target = try alloc.alloc(u8, target_size);
|
const target = try alloc.alloc(u8, target_size);
|
||||||
|
errdefer alloc.free(target);
|
||||||
try rdr.readSliceEndian(u8, target, .little);
|
try rdr.readSliceEndian(u8, target, .little);
|
||||||
|
|
||||||
return .{
|
return .{
|
||||||
@@ -239,6 +242,7 @@ pub const ExtSymlink = struct {
|
|||||||
|
|
||||||
fn init(alloc: std.mem.Allocator, rdr: *Reader) !ExtSymlink {
|
fn init(alloc: std.mem.Allocator, rdr: *Reader) !ExtSymlink {
|
||||||
const sym: Symlink = try .init(alloc, rdr);
|
const sym: Symlink = try .init(alloc, rdr);
|
||||||
|
errdefer alloc.free(sym.target);
|
||||||
|
|
||||||
var xattr_idx: u32 = undefined;
|
var xattr_idx: u32 = undefined;
|
||||||
try rdr.readSliceEndian(u32, @ptrCast(&xattr_idx), .little);
|
try rdr.readSliceEndian(u32, @ptrCast(&xattr_idx), .little);
|
||||||
|
|||||||
+2
-2
@@ -58,10 +58,10 @@ pub fn Table(comptime T: anytype) type {
|
|||||||
return values.*[block_idx];
|
return values.*[block_idx];
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getBlock(alloc: std.mem.Allocator, data: []u8, decomp: Decomp.Fn, table_start: u64, table_num: u32, block_idx: u32) ![]T {
|
fn getBlock(alloc: std.mem.Allocator, data: []u8, decomp: Decomp.Fn, table_start: u64, table_num: u32, block_idx: u32) ![]T {
|
||||||
const offset: u64 = std.mem.readInt(u64, data[table_start + (block_idx * 8) ..][0..8], .little);
|
const offset: u64 = std.mem.readInt(u64, data[table_start + (block_idx * 8) ..][0..8], .little);
|
||||||
|
|
||||||
const block = try alloc.alloc(T, if (block_idx == (table_num - 1 / VALUES_PER_BLOCK))
|
const block = try alloc.alloc(T, if (block_idx == (table_num - 1) / VALUES_PER_BLOCK)
|
||||||
table_num % VALUES_PER_BLOCK
|
table_num % VALUES_PER_BLOCK
|
||||||
else
|
else
|
||||||
VALUES_PER_BLOCK);
|
VALUES_PER_BLOCK);
|
||||||
|
|||||||
+2
-1
@@ -44,7 +44,8 @@ fn advance(self: *MetadataReader) Reader.Error!void {
|
|||||||
errdefer self.interface.end = 0;
|
errdefer self.interface.end = 0;
|
||||||
|
|
||||||
const hdr: Header = @bitCast(std.mem.readInt(u16, self.data[self.cur_offset..][0..2], .little));
|
const hdr: Header = @bitCast(std.mem.readInt(u16, self.data[self.cur_offset..][0..2], .little));
|
||||||
defer self.cur_offset += hdr.size;
|
if (hdr.size == 0 or hdr.size > 8192) return Reader.Error.ReadFailed;
|
||||||
|
defer self.cur_offset += hdr.size + 2;
|
||||||
|
|
||||||
const block = self.data[self.cur_offset + 2 ..][0..hdr.size];
|
const block = self.data[self.cur_offset + 2 ..][0..hdr.size];
|
||||||
|
|
||||||
|
|||||||
+29
-11
@@ -25,7 +25,7 @@ test "Basics" {
|
|||||||
const TestFile = "Start.exe";
|
const TestFile = "Start.exe";
|
||||||
const TestFileExtractLocation = "testing/Start.exe";
|
const TestFileExtractLocation = "testing/Start.exe";
|
||||||
|
|
||||||
test "ExtractSingleFile" {
|
test "ExtractSingleFileMT" {
|
||||||
const io = testing.io;
|
const io = testing.io;
|
||||||
const alloc = testing.allocator;
|
const alloc = testing.allocator;
|
||||||
|
|
||||||
@@ -43,13 +43,11 @@ test "ExtractSingleFile" {
|
|||||||
try start_exe.extract(alloc, io, TestFileExtractLocation, .default);
|
try start_exe.extract(alloc, io, TestFileExtractLocation, .default);
|
||||||
}
|
}
|
||||||
|
|
||||||
const TestFullExtractLocationMT = "testing/TestExtractMT";
|
test "ExtractSingleFileST" {
|
||||||
|
|
||||||
test "ExtractCompleteArchiveMultiThreaded" {
|
|
||||||
const io = testing.io;
|
const io = testing.io;
|
||||||
const alloc = testing.allocator;
|
const alloc = testing.allocator;
|
||||||
|
|
||||||
Io.Dir.cwd().deleteFile(io, TestFullExtractLocationMT) catch {};
|
Io.Dir.cwd().deleteFile(io, TestFileExtractLocation) catch {};
|
||||||
|
|
||||||
var archive_file = try Io.Dir.cwd().openFile(io, TestArchive, .{});
|
var archive_file = try Io.Dir.cwd().openFile(io, TestArchive, .{});
|
||||||
defer archive_file.close(io);
|
defer archive_file.close(io);
|
||||||
@@ -57,7 +55,10 @@ test "ExtractCompleteArchiveMultiThreaded" {
|
|||||||
var archive: Archive = try .init(io, archive_file, 0);
|
var archive: Archive = try .init(io, archive_file, 0);
|
||||||
defer archive.deinit(io);
|
defer archive.deinit(io);
|
||||||
|
|
||||||
try archive.extract(alloc, io, TestFullExtractLocationMT, .default);
|
var start_exe = try archive.open(alloc, TestFile);
|
||||||
|
defer start_exe.deinit();
|
||||||
|
|
||||||
|
try start_exe.extract(alloc, io, TestFileExtractLocation, .single_threaded_default);
|
||||||
}
|
}
|
||||||
|
|
||||||
const TestFullExtractLocationSTOption = "testing/TestExtractSTOption";
|
const TestFullExtractLocationSTOption = "testing/TestExtractSTOption";
|
||||||
@@ -77,13 +78,13 @@ test "ExtractCompleteArchiveSingleThreadedOption" {
|
|||||||
try archive.extract(alloc, io, TestFullExtractLocationSTOption, .single_threaded_default);
|
try archive.extract(alloc, io, TestFullExtractLocationSTOption, .single_threaded_default);
|
||||||
}
|
}
|
||||||
|
|
||||||
const TestFullExtractLocationSTIo = "testing/TestExtractSTIo";
|
const TestFullExtractLocationMT = "testing/TestExtractMT";
|
||||||
|
|
||||||
test "ExtractCompleteArchiveSingleThreadedIo" {
|
test "ExtractCompleteArchiveMultiThreaded" {
|
||||||
const io = Io.Threaded.global_single_threaded.io();
|
const io = testing.io;
|
||||||
const alloc = testing.allocator;
|
const alloc = testing.allocator;
|
||||||
|
|
||||||
Io.Dir.cwd().deleteFile(io, TestFullExtractLocationSTIo) catch {};
|
Io.Dir.cwd().deleteFile(io, TestFullExtractLocationMT) catch {};
|
||||||
|
|
||||||
var archive_file = try Io.Dir.cwd().openFile(io, TestArchive, .{});
|
var archive_file = try Io.Dir.cwd().openFile(io, TestArchive, .{});
|
||||||
defer archive_file.close(io);
|
defer archive_file.close(io);
|
||||||
@@ -91,9 +92,26 @@ test "ExtractCompleteArchiveSingleThreadedIo" {
|
|||||||
var archive: Archive = try .init(io, archive_file, 0);
|
var archive: Archive = try .init(io, archive_file, 0);
|
||||||
defer archive.deinit(io);
|
defer archive.deinit(io);
|
||||||
|
|
||||||
try archive.extract(alloc, io, TestFullExtractLocationSTIo, .default);
|
try archive.extract(alloc, io, TestFullExtractLocationMT, .default);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const TestFullExtractLocationSTIo = "testing/TestExtractSTIo";
|
||||||
|
|
||||||
|
// test "ExtractCompleteArchiveSingleThreadedIo" {
|
||||||
|
// const io = Io.Threaded.global_single_threaded.io();
|
||||||
|
// const alloc = testing.allocator;
|
||||||
|
|
||||||
|
// Io.Dir.cwd().deleteFile(io, TestFullExtractLocationSTIo) catch {};
|
||||||
|
|
||||||
|
// var archive_file = try Io.Dir.cwd().openFile(io, TestArchive, .{});
|
||||||
|
// defer archive_file.close(io);
|
||||||
|
|
||||||
|
// var archive: Archive = try .init(io, archive_file, 0);
|
||||||
|
// defer archive.deinit(io);
|
||||||
|
|
||||||
|
// try archive.extract(alloc, io, TestFullExtractLocationSTIo, .default);
|
||||||
|
// }
|
||||||
|
|
||||||
const LinuxPATestCorrectSuperblock: Archive.Superblock = .{
|
const LinuxPATestCorrectSuperblock: Archive.Superblock = .{
|
||||||
.magic = std.mem.readInt(u32, "hsqs", .little),
|
.magic = std.mem.readInt(u32, "hsqs", .little),
|
||||||
.inode_count = 2974,
|
.inode_count = 2974,
|
||||||
|
|||||||
@@ -61,14 +61,13 @@ pub fn ProtectedMap(comptime K: anytype, comptime T: anytype, comptime create_fn
|
|||||||
self.mut.lockSharedUncancelable(io);
|
self.mut.lockSharedUncancelable(io);
|
||||||
defer self.mut.unlockShared(io);
|
defer self.mut.unlockShared(io);
|
||||||
|
|
||||||
if (@TypeOf(CreateError) == void) {
|
res.value_ptr.value = if (@TypeOf(CreateError) == void)
|
||||||
res.value_ptr.value = @call(.auto, create_fn, create_fn_args);
|
@call(.auto, create_fn, create_fn_args)
|
||||||
} else {
|
else
|
||||||
res.value_ptr.value = @call(.auto, create_fn, create_fn_args) catch |err| {
|
@call(.auto, create_fn, create_fn_args) catch |err| {
|
||||||
res.value_ptr.err = err;
|
res.value_ptr.err = err;
|
||||||
return err;
|
return err;
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
return &res.value_ptr.value;
|
return &res.value_ptr.value;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user