From 80c4c8df5e4dbdd1735f978c10500f99323c1d73 Mon Sep 17 00:00:00 2001 From: Caleb Gardner Date: Wed, 17 Jun 2026 05:23:39 -0500 Subject: [PATCH] Bug fixes --- .zed/debug.json | 23 ++- build.zig | 17 +- src/bin/unsquashfs.zig | 16 +- src/c_decomp.zig | 4 +- src/data/extractor.zig | 8 +- src/extract-multi.zig | 70 +++++-- src/extract-single.zig | 365 +++++++++++++++++++++---------------- src/inode.zig | 4 + src/lookup.zig | 4 +- src/meta_rdr.zig | 3 +- src/test.zig | 40 ++-- src/util/protected_map.zig | 9 +- 12 files changed, 353 insertions(+), 210 deletions(-) diff --git a/.zed/debug.json b/.zed/debug.json index 1bcdda2..7b086d6 100644 --- a/.zed/debug.json +++ b/.zed/debug.json @@ -11,7 +11,7 @@ "build": { "command": "zig", - "args": ["build", "-Duse_c_libs=true", "-Ddebug=true"], + "args": ["build", "-Ddebug=true"], }, "program": "zig-out/bin/unsquashfs", @@ -22,4 +22,25 @@ "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", + ], + }, ] diff --git a/build.zig b/build.zig index bb21525..91f7be8 100644 --- a/build.zig +++ b/build.zig @@ -12,10 +12,10 @@ pub fn build(b: *std.Build) !void { const version: std.SemanticVersion = try .parse(version_string_option); - const build_options = b.addOptions(); - build_options.addOption(bool, "use_zig_decomp", use_zig_decomp); - build_options.addOption(bool, "allow_lzo", allow_lzo); - build_options.addOption(std.SemanticVersion, "version", version); + const options = b.addOptions(); + options.addOption(bool, "use_zig_decomp", use_zig_decomp); + options.addOption(bool, "allow_lzo", allow_lzo); + options.addOption(std.SemanticVersion, "version", version); const c = b.addTranslateC(.{ .optimize = optimize, @@ -44,7 +44,7 @@ pub fn build(b: *std.Build) !void { .valgrind = debug, .imports = &.{ .{ .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); + const unsquashfs_options = b.addOptions(); + unsquashfs_options.addOption(std.SemanticVersion, "version", version); + const exe = b.addExecutable(.{ .name = "unsquashfs", .use_llvm = debug, @@ -80,11 +83,13 @@ pub fn build(b: *std.Build) !void { .valgrind = debug, .imports = &.{ .{ .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(.{ .root_module = lib.root_module, .use_llvm = debug, diff --git a/src/bin/unsquashfs.zig b/src/bin/unsquashfs.zig index 4b9ee4c..60515c6 100644 --- a/src/bin/unsquashfs.zig +++ b/src/bin/unsquashfs.zig @@ -2,7 +2,7 @@ const std = @import("std"); const Io = std.Io; const Writer = Io.Writer; const builtin = @import("builtin"); -const build = @import("build_options"); +const build = @import("build"); const squashfs = @import("squashfs"); @@ -41,7 +41,7 @@ var force: bool = false; pub fn main(init: std.process.Init) !void { const io = init.io; - const alloc = init.alloc; + const alloc = init.gpa; var stdout = Io.File.stdout(); var out = stdout.writer(io, &[0]u8{}); @@ -54,23 +54,23 @@ pub fn main(init: std.process.Init) !void { return; } 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. - defer arc.deinit(); + defer arc.deinit(io); const options: squashfs.ExtractionOptions = .{ - .single_threaded = threads == 1, + .single_threaded = (threads == 1), .verbose = verbose, .verbose_writer = if (verbose) &out.interface else null, .ignore_xattr = ignore_xattrs, .ignore_permissions = ignore_permissions, }; if (force) - try std.fs.cwd().deleteTree(ext_loc); + try Io.Dir.cwd().deleteTree(io, ext_loc); if (threads > 1) { var limited_io = Io.Threaded.init(alloc, .{ .argv0 = .init(init.minimal.args), - .async_limit = threads, - .concurrent_limit = threads, + .async_limit = @enumFromInt(threads), + .concurrent_limit = @enumFromInt(threads), .environ = init.minimal.environ, }); try arc.extract(alloc, limited_io.io(), ext_loc, options); //TODO: Handle error gracefully. diff --git a/src/c_decomp.zig b/src/c_decomp.zig index c4832fb..681947a 100644 --- a/src/c_decomp.zig +++ b/src/c_decomp.zig @@ -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 { 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 res; } diff --git a/src/data/extractor.zig b/src/data/extractor.zig index 8a2e5a8..82fd54d 100644 --- a/src/data/extractor.zig +++ b/src/data/extractor.zig @@ -76,7 +76,8 @@ fn blockThread(self: Extractor, alloc: std.mem.Allocator, io: Io, map_data: []u8 self.size % self.block_size else self.block_size; - const offset = self.block_size * block_idx; + + const offset = block_idx * self.block_size; const block = self.blocks[block_idx]; @@ -92,17 +93,22 @@ fn blockThread(self: Extractor, alloc: std.mem.Allocator, io: Io, map_data: []u8 return; } + std.debug.print("offset: {} start: {} block: {any}\n", .{ read_offset, self.start, block }); + if (self.cache != null) { 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) { error.Canceled => return error.Canceled, else => |e| err.* = e, } return; }; + std.debug.print("cached block size: {} should be {}\n", .{ decomp_block.len, size }); @memcpy(map_data[offset..][0..size], decomp_block[0..size]); } else { _ = self.decomp(alloc, data, map_data[offset..][0..size]) catch |inner_err| { + std.debug.print("data decomp err: {}\n", .{inner_err}); err.* = inner_err; }; } diff --git a/src/extract-multi.zig b/src/extract-multi.zig index 6bb87bb..d972fab 100644 --- a/src/extract-multi.zig +++ b/src/extract-multi.zig @@ -2,6 +2,7 @@ const std = @import("std"); const Io = std.Io; const DataExtractor = @import("data/extractor.zig"); +const DataReader = @import("data/reader.zig"); const Decomp = @import("decomp.zig"); const Directory = @import("directory.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; defer dirs.deinit(alloc); errdefer while (dirs.popMax()) |d| - alloc.free(d.path); + if (d.hdr.num != start_num) alloc.free(d.path); while (true) { const value: ReturnUnion = try sel.await(); @@ -178,7 +179,7 @@ fn extractDir( origin: bool, ) Error!PathReturn { defer if (!origin) inode.deinit(alloc); - errdefer alloc.free(path); + errdefer if (!origin) alloc.free(path); var ret: PathReturn = .{ .hdr = inode.hdr, @@ -189,7 +190,7 @@ fn extractDir( var dir: Directory = switch (inode.data) { .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); break :blk try Directory.init(alloc, &meta.interface, d.size); @@ -197,7 +198,7 @@ fn extractDir( .ext_dir => |d| blk: { 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); break :blk try Directory.init(alloc, &meta.interface, d.size); @@ -237,7 +238,7 @@ fn extractFile( origin: bool, ) Error!PathReturn { defer if (!origin) inode.deinit(alloc); - errdefer alloc.free(path); + errdefer if (!origin) alloc.free(path); try io.checkCancel(); @@ -246,10 +247,53 @@ fn extractFile( .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: { - var rdr: DataExtractor = .init(data, decomp, block_size, f.blocks, f.size, f.block_start); - rdr.addCache(cache); + var rdr: DataReader = .init(alloc, data, decomp, block_size, f.blocks, f.size, f.block_start); + rdr.addCache(io, cache); if (f.frag_idx == 0xFFFFFFFF) break :blk rdr; @@ -265,8 +309,8 @@ fn extractFile( .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); + var rdr: DataReader = .init(alloc, data, decomp, block_size, f.blocks, f.size, f.block_start); + rdr.addCache(io, cache); if (f.frag_idx == 0xFFFFFFFF) break :blk rdr; @@ -285,7 +329,9 @@ fn extractFile( var atomic = try Io.Dir.cwd().createFileAtomic(io, path, .{}); 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); @@ -372,7 +418,7 @@ const ReturnUnion = union(enum) { }; 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 { hdr: Inode.Header, diff --git a/src/extract-single.zig b/src/extract-single.zig index 3a413de..3facbed 100644 --- a/src/extract-single.zig +++ b/src/extract-single.zig @@ -36,178 +36,24 @@ pub fn extract( var xattr_table: XattrTable = .init(alloc, data, decomp, super.xattr_start); defer xattr_table.deinit(); - return extractReal( - alloc, - io, - super, - data, - decomp, - &cache, - &frag_table, - &id_table, - &xattr_table, - inode, - path, - options, - true, - ); + return switch (inode.hdr.type) { + .file, .ext_file => try extractFile(alloc, io, super, data, decomp, &cache, &frag_table, &id_table, &xattr_table, inode, path, options), + .dir, .ext_dir => try extractDir(alloc, io, super, data, decomp, &cache, &frag_table, &id_table, &xattr_table, inode, path, options), + .symlink, .ext_symlink => try extractSymlink(io, inode, path), + else => try extractNod(alloc, io, &id_table, &xattr_table, inode, path, options), + }; } -pub fn extractReal( +fn setMetadata( 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, - origin: bool, + xattr_idx: ?u32, ) !void { - defer if (!origin) { - alloc.free(path); - inode.deinit(alloc); - }; - - var xattr_idx: ?u32 = null; - - switch (inode.data) { - .dir, .ext_dir => { - try Io.Dir.cwd().createDirPath(io, path); - - var dir: Directory = switch (inode.data) { - .dir => |d| blk: { - var meta: MetadaReader = .init(alloc, data, decomp, d.block_start); - try meta.interface.discardAll(d.block_offset); - - break :blk try Directory.init(alloc, &meta.interface, d.size); - }, - .ext_dir => |d| blk: { - if (d.xattr_idx != 0xFFFFFFFF) xattr_idx = d.xattr_idx; - - var meta: MetadaReader = .init(alloc, data, decomp, d.block_start); - try meta.interface.discardAll(d.block_offset); - - break :blk try Directory.init(alloc, &meta.interface, d.size); - }, - else => unreachable, - }; - defer dir.deinit(alloc); - - for (dir.entries) |entry| { - var new_inode: Inode = try .initEntry(alloc, data, decomp, super.inode_start, super.block_size, entry); - - const new_path = std.mem.concat(alloc, u8, &.{ path, "/", entry.name }) catch |err| { - new_inode.deinit(alloc); - return err; - }; - - try extractReal(alloc, io, super, data, decomp, cache, frag_table, id_table, xattr_table, new_inode, new_path, options, false); - } - }, - .file, .ext_file => { - var rdr: DataReader = switch (inode.data) { - .file => |f| blk: { - var rdr: DataReader = .init(alloc, data, decomp, super.block_size, f.blocks, f.size, f.block_start); - rdr.addCache(io, 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) xattr_idx = f.xattr_idx; - - var rdr: DataReader = .init(alloc, data, decomp, super.block_size, f.blocks, f.size, f.block_start); - rdr.addCache(io, 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); - - var writer = atomic.file.writer(io, &[0]u8{}); - _ = try rdr.interface.streamRemaining(&writer.interface); - try writer.flush(); - - try atomic.link(io); - }, - .symlink => |s| return Io.Dir.cwd().symLink(io, s.target, path, .{}), - .ext_symlink => |s| return Io.Dir.cwd().symLink(io, s.target, path, .{}), - else => { - var dev: u32 = 0; - var mode: u32 = undefined; - - const DT = std.posix.DT; - - switch (inode.data) { - .char_dev => |d| { - dev = d.device; - mode = DT.CHR; - }, - .block_dev => |d| { - dev = d.device; - mode = DT.BLK; - }, - .ext_char_dev => |d| { - if (d.xattr_idx != 0xFFFFFFFF) xattr_idx = d.xattr_idx; - - dev = d.device; - mode = DT.CHR; - }, - .ext_block_dev => |d| { - if (d.xattr_idx != 0xFFFFFFFF) xattr_idx = d.xattr_idx; - - dev = d.device; - mode = DT.BLK; - }, - .fifo => mode = DT.FIFO, - .socket => mode = DT.SOCK, - .ext_fifo => |i| { - if (i.xattr_idx != 0xFFFFFFFF) xattr_idx = i.xattr_idx; - - mode = DT.FIFO; - }, - .ext_socket => |i| { - if (i.xattr_idx != 0xFFFFFFFF) xattr_idx = i.xattr_idx; - - mode = DT.SOCK; - }, - else => unreachable, - } - - const sentinel_path = try alloc.dupeSentinel(u8, path, 0); - defer alloc.free(sentinel_path); - - const res = std.os.linux.mknod(sentinel_path, mode, dev); - if (res != 0) - return error.MknodError; - }, - } - if (options.ignore_permissions and (options.ignore_xattr or xattr_idx == null)) return; var fil: Io.File = try Io.Dir.cwd().openFile(io, path, .{}); @@ -231,3 +77,198 @@ pub fn extractReal( } } } + +fn extractDir( + 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 dir: {s}\n", .{path}); + defer std.debug.print("end extract dir: {s}\n", .{path}); + + var xattr_idx: ?u32 = null; + + try Io.Dir.cwd().createDirPath(io, path); + + var dir: Directory = switch (inode.data) { + .dir => |d| blk: { + var meta: MetadaReader = .init(alloc, data, decomp, d.block_start + super.dir_start); + try meta.interface.discardAll(d.block_offset); + + break :blk try Directory.init(alloc, &meta.interface, d.size); + }, + .ext_dir => |d| blk: { + if (d.xattr_idx != 0xFFFFFFFF) xattr_idx = d.xattr_idx; + + var meta: MetadaReader = .init(alloc, data, decomp, d.block_start + super.dir_start); + try meta.interface.discardAll(d.block_offset); + + break :blk try Directory.init(alloc, &meta.interface, d.size); + }, + else => unreachable, + }; + defer dir.deinit(alloc); + + for (dir.entries) |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 = try std.mem.concat(alloc, u8, &.{ path, "/", entry.name }); + defer alloc.free(new_path); + + 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), + } + } + + 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) { + .file => |f| blk: { + var rdr: DataReader = .init(alloc, data, decomp, super.block_size, f.blocks, f.size, f.block_start); + rdr.addCache(io, 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) xattr_idx = f.xattr_idx; + + var rdr: DataReader = .init(alloc, data, decomp, super.block_size, f.blocks, f.size, f.block_start); + rdr.addCache(io, 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); + + var writer = atomic.file.writer(io, &[0]u8{}); + _ = try rdr.interface.streamRemaining(&writer.interface); + try writer.flush(); + + try atomic.link(io); + + try setMetadata(alloc, io, id_table, xattr_table, inode, path, options, xattr_idx); +} +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 mode: u32 = undefined; + + var xattr_idx: ?u32 = null; + + const DT = std.posix.DT; + + switch (inode.data) { + .char_dev => |d| { + dev = d.device; + mode = DT.CHR; + }, + .block_dev => |d| { + dev = d.device; + mode = DT.BLK; + }, + .ext_char_dev => |d| { + if (d.xattr_idx != 0xFFFFFFFF) xattr_idx = d.xattr_idx; + + dev = d.device; + mode = DT.CHR; + }, + .ext_block_dev => |d| { + if (d.xattr_idx != 0xFFFFFFFF) xattr_idx = d.xattr_idx; + + dev = d.device; + mode = DT.BLK; + }, + .fifo => mode = DT.FIFO, + .socket => mode = DT.SOCK, + .ext_fifo => |i| { + if (i.xattr_idx != 0xFFFFFFFF) xattr_idx = i.xattr_idx; + + mode = DT.FIFO; + }, + .ext_socket => |i| { + if (i.xattr_idx != 0xFFFFFFFF) xattr_idx = i.xattr_idx; + + mode = DT.SOCK; + }, + else => unreachable, + } + + const sentinel_path = try alloc.dupeSentinel(u8, path, 0); + defer alloc.free(sentinel_path); + + const res = std.os.linux.mknod(sentinel_path, mode, dev); + if (res != 0) + return error.MknodError; + + try setMetadata(alloc, io, id_table, xattr_table, inode, path, options, xattr_idx); +} diff --git a/src/inode.zig b/src/inode.zig index 725be28..9644cb4 100644 --- a/src/inode.zig +++ b/src/inode.zig @@ -166,6 +166,7 @@ pub const File = struct { blocks_num += 1; const blocks = try alloc.alloc(DataBlock, blocks_num); + errdefer alloc.free(blocks); try rdr.readSliceEndian(DataBlock, blocks, .little); return .{ @@ -199,6 +200,7 @@ pub const ExtFile = struct { blocks_num += 1; const blocks = try alloc.alloc(DataBlock, blocks_num); + errdefer alloc.free(blocks); try rdr.readSliceEndian(DataBlock, blocks, .little); return .{ @@ -224,6 +226,7 @@ pub const Symlink = struct { const target_size = std.mem.readInt(u32, data[4..], .little); const target = try alloc.alloc(u8, target_size); + errdefer alloc.free(target); try rdr.readSliceEndian(u8, target, .little); return .{ @@ -239,6 +242,7 @@ pub const ExtSymlink = struct { fn init(alloc: std.mem.Allocator, rdr: *Reader) !ExtSymlink { const sym: Symlink = try .init(alloc, rdr); + errdefer alloc.free(sym.target); var xattr_idx: u32 = undefined; try rdr.readSliceEndian(u32, @ptrCast(&xattr_idx), .little); diff --git a/src/lookup.zig b/src/lookup.zig index c763ef7..0483ab8 100644 --- a/src/lookup.zig +++ b/src/lookup.zig @@ -58,10 +58,10 @@ pub fn Table(comptime T: anytype) type { 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 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 else VALUES_PER_BLOCK); diff --git a/src/meta_rdr.zig b/src/meta_rdr.zig index 9dc4a8c..1a19a71 100644 --- a/src/meta_rdr.zig +++ b/src/meta_rdr.zig @@ -44,7 +44,8 @@ fn advance(self: *MetadataReader) Reader.Error!void { errdefer self.interface.end = 0; 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]; diff --git a/src/test.zig b/src/test.zig index 98a153e..0b11f91 100644 --- a/src/test.zig +++ b/src/test.zig @@ -25,7 +25,7 @@ test "Basics" { const TestFile = "Start.exe"; const TestFileExtractLocation = "testing/Start.exe"; -test "ExtractSingleFile" { +test "ExtractSingleFileMT" { const io = testing.io; const alloc = testing.allocator; @@ -43,13 +43,11 @@ test "ExtractSingleFile" { try start_exe.extract(alloc, io, TestFileExtractLocation, .default); } -const TestFullExtractLocationMT = "testing/TestExtractMT"; - -test "ExtractCompleteArchiveMultiThreaded" { +test "ExtractSingleFileST" { const io = testing.io; 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, .{}); defer archive_file.close(io); @@ -57,7 +55,10 @@ test "ExtractCompleteArchiveMultiThreaded" { var archive: Archive = try .init(io, archive_file, 0); 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"; @@ -77,13 +78,13 @@ test "ExtractCompleteArchiveSingleThreadedOption" { try archive.extract(alloc, io, TestFullExtractLocationSTOption, .single_threaded_default); } -const TestFullExtractLocationSTIo = "testing/TestExtractSTIo"; +const TestFullExtractLocationMT = "testing/TestExtractMT"; -test "ExtractCompleteArchiveSingleThreadedIo" { - const io = Io.Threaded.global_single_threaded.io(); +test "ExtractCompleteArchiveMultiThreaded" { + const io = testing.io; 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, .{}); defer archive_file.close(io); @@ -91,9 +92,26 @@ test "ExtractCompleteArchiveSingleThreadedIo" { var archive: Archive = try .init(io, archive_file, 0); 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 = .{ .magic = std.mem.readInt(u32, "hsqs", .little), .inode_count = 2974, diff --git a/src/util/protected_map.zig b/src/util/protected_map.zig index a7a4fe0..d943dd1 100644 --- a/src/util/protected_map.zig +++ b/src/util/protected_map.zig @@ -61,14 +61,13 @@ pub fn ProtectedMap(comptime K: anytype, comptime T: anytype, comptime create_fn self.mut.lockSharedUncancelable(io); defer self.mut.unlockShared(io); - if (@TypeOf(CreateError) == void) { - res.value_ptr.value = @call(.auto, create_fn, create_fn_args); - } else { - res.value_ptr.value = @call(.auto, create_fn, create_fn_args) catch |err| { + res.value_ptr.value = if (@TypeOf(CreateError) == void) + @call(.auto, create_fn, create_fn_args) + else + @call(.auto, create_fn, create_fn_args) catch |err| { res.value_ptr.err = err; return err; }; - } return &res.value_ptr.value; }