From 238e69acdd9614688978f9f0523b16b57f449853 Mon Sep 17 00:00:00 2001 From: "Caleb J. Gardner" Date: Wed, 3 Jun 2026 19:16:34 -0500 Subject: [PATCH] Switched to single-threaded extraction for easier debugging. Fixed a few issues --- src/decomp_cache.zig | 2 - src/extract.zig | 369 ++++++++++++++----------------------------- src/xattr.zig | 5 +- 3 files changed, 122 insertions(+), 254 deletions(-) diff --git a/src/decomp_cache.zig b/src/decomp_cache.zig index 4b64c8e..5d94b2e 100644 --- a/src/decomp_cache.zig +++ b/src/decomp_cache.zig @@ -91,8 +91,6 @@ pub fn finished(self: *DecompCache, io: Io, offset: u64) void { return; } - self.mut.lockUncancelable(io); - defer self.mut.unlock(io); const use = cache.?.usage.fetchSub(1, .acquire); if (use == 0) self.cond.broadcast(io); diff --git a/src/extract.zig b/src/extract.zig index 09150a3..6889bd4 100644 --- a/src/extract.zig +++ b/src/extract.zig @@ -15,45 +15,16 @@ const XattrTable = @import("xattr.zig"); pub fn extract(alloc: std.mem.Allocator, io: Io, inode: Inode, cache: *DecompCache, super: Superblock, ext_loc: []const u8, options: ExtractionOptions) !void { const path = std.mem.trim(u8, ext_loc, "/"); - var buf: [1000]ReturnUnion = undefined; - var sel: Io.Select(ReturnUnion) = .init(io, &buf); - - defer { - std.debug.print("starting cancel loop...\n", .{}); - while (sel.cancel()) |ret| { - std.debug.print("HELLO!\n", .{}); - switch (ret) { - .dir_ret => |d| { - const res = d catch continue; - if (!res.origin) - alloc.free(res.path); - alloc.destroy(res.sub_files); - }, - .file_ret => |f| { - const res = f catch continue; - if (!res.origin) - alloc.free(res.path); - }, - else => {}, - } - } - std.debug.print("goodbye\n", .{}); - } - var id_table: Lookup.Table(u16) = .init(alloc, cache, super.id_start, super.id_count); - defer id_table.deinit(); + defer id_table.deinit(io); var xattr_table: XattrTable = try .init(alloc, cache, super.xattr_start); - defer xattr_table.deinit(); - - var ret_loop = io.async(returnLoop, .{ alloc, io, &id_table, &xattr_table, &sel, options }); + defer xattr_table.deinit(io); var frag_table: Lookup.Table(Lookup.FragmentEntry) = .init(alloc, cache, super.frag_start, super.frag_count); - defer frag_table.deinit(); + defer frag_table.deinit(io); - try extractReal(alloc, io, cache, super, &sel, &frag_table, path, inode, null, true); - - try ret_loop.await(io); + try extractReal(alloc, io, cache, super, &frag_table, &id_table, &xattr_table, options, path, inode, true); } fn extractReal( @@ -61,39 +32,32 @@ fn extractReal( io: Io, cache: *DecompCache, super: Superblock, - sel: *Io.Select(ReturnUnion), frag_table: *Lookup.Table(Lookup.FragmentEntry), + id_table: *Lookup.Table(u16), + xattr_table: *XattrTable, + options: ExtractionOptions, path: []const u8, inode: Inode, - parent: ?*Atomic(usize), origin: bool, ) Error!void { - io.checkCancel() catch |err| { - if (parent != null) _ = parent.?.fetchSub(1, .acquire); - return err; + defer if (!origin) { + inode.deinit(alloc); + alloc.free(path); }; - switch (inode.data) { - .dir, .ext_dir => sel.async( - .dir_ret, - extractDir, - .{ alloc, io, cache, super, sel, frag_table, path, inode, parent, origin }, - ), - .file, .ext_file => sel.async( - .file_ret, - extractFile, - .{ alloc, io, cache, super.block_size, frag_table, path, inode, parent, origin }, - ), - .symlink, .ext_symlink => sel.async( - .void_ret, - extractSymlink, - .{ alloc, io, path, inode, parent, origin }, - ), - else => sel.async( - .file_ret, - extractNod, - .{ alloc, path, inode, parent, origin }, - ), + .dir, .ext_dir => { + const res = try extractDir(alloc, io, cache, super, frag_table, id_table, xattr_table, options, path, inode); + try res.finish(alloc, io, id_table, xattr_table, options); + }, + .file, .ext_file => { + const res = try extractFile(io, cache, super.block_size, frag_table, path, inode); + try res.finish(alloc, io, id_table, xattr_table, options); + }, + .symlink, .ext_symlink => try extractSymlink(io, path, inode), + else => { + const res = try extractNod(alloc, path, inode); + try res.finish(alloc, io, id_table, xattr_table, options); + }, } } @@ -102,21 +66,13 @@ fn extractDir( io: Io, cache: *DecompCache, super: Superblock, - sel: *Io.Select(ReturnUnion), frag_table: *Lookup.Table(Lookup.FragmentEntry), + id_table: *Lookup.Table(u16), + xattr_table: *XattrTable, + options: ExtractionOptions, path: []const u8, inode: Inode, - parent: ?*Atomic(usize), - origin: bool, -) Error!DirReturn { - std.debug.print("started extract of {s}\n", .{path}); - defer { - if (parent != null) - _ = parent.?.fetchSub(1, .acquire); - if (!origin) inode.deinit(alloc); - } - errdefer if (!origin) alloc.free(path); - +) Error!DirReturn(false) { try Io.Dir.cwd().createDirPath(io, path); const dir = inode.directory(alloc, io, cache, super.dir_start) catch |err| switch (err) { @@ -125,18 +81,9 @@ fn extractDir( }; defer dir.deinit(alloc); - const sub_files = try alloc.create(Atomic(usize)); - sub_files.* = .init(dir.entries.len); - - const ret: DirReturn = .{ + const ret: DirReturn(false) = .{ + .hdr = inode.hdr, .path = path, - .sub_files = sub_files, - .origin = origin, - - .uid_idx = inode.hdr.uid_idx, - .gid_idx = inode.hdr.gid_idx, - .mod_time = inode.hdr.mod_time, - .permissions = inode.hdr.permission, .xattr_idx = switch (inode.data) { .ext_dir => |d| if (d.xattr_idx != 0xFFFFFFFF) d.xattr_idx else null, @@ -146,56 +93,42 @@ fn extractDir( for (dir.entries) |entry| { const new_inode: Inode = try .initDirEntry(alloc, io, cache, super.inode_start, super.block_size, entry); - errdefer new_inode.deinit(alloc); - const new_path = try std.mem.concat(alloc, u8, &.{ path, "/", entry.name }); - errdefer alloc.free(new_path); + const new_path = std.mem.concat(alloc, u8, &.{ path, "/", entry.name }) catch |err| { + new_inode.deinit(alloc); + return err; + }; try extractReal( alloc, io, cache, super, - sel, frag_table, + id_table, + xattr_table, + options, new_path, new_inode, - sub_files, false, ); } return ret; } fn extractFile( - alloc: std.mem.Allocator, io: Io, cache: *DecompCache, block_size: u32, frag_table: *Lookup.Table(Lookup.FragmentEntry), path: []const u8, inode: Inode, - parent: ?*Atomic(usize), - origin: bool, -) Error!FileReturn { - std.debug.print("started extract of {s}\n", .{path}); - defer { - if (parent != null) - _ = parent.?.fetchSub(1, .acquire); - if (!origin) inode.deinit(alloc); - } - errdefer if (!origin) alloc.free(path); - +) Error!FileReturn(false) { var atomic = try Io.Dir.cwd().createFileAtomic(io, path, .{}); defer atomic.deinit(io); - var ret: FileReturn = .{ + var ret: FileReturn(false) = .{ + .hdr = inode.hdr, .path = path, - .origin = origin, - - .uid_idx = inode.hdr.uid_idx, - .gid_idx = inode.hdr.gid_idx, - .permissions = inode.hdr.permission, - .mod_time = inode.hdr.mod_time, }; const data: DataExtractor = switch (inode.data) { @@ -235,17 +168,7 @@ fn extractFile( return ret; } -fn extractSymlink(alloc: std.mem.Allocator, io: Io, path: []const u8, inode: Inode, parent: ?*Atomic(usize), origin: bool) Error!void { - std.debug.print("started extract of {s}\n", .{path}); - defer { - if (parent != null) - _ = parent.?.fetchSub(1, .acquire); - if (!origin) { - inode.deinit(alloc); - alloc.free(path); - } - } - +fn extractSymlink(io: Io, path: []const u8, inode: Inode) Error!void { const target = switch (inode.data) { .symlink => |s| s.target, .ext_symlink => |s| s.target, @@ -254,23 +177,10 @@ fn extractSymlink(alloc: std.mem.Allocator, io: Io, path: []const u8, inode: Ino try Io.Dir.cwd().symLink(io, target, path, .{}); } -fn extractNod(alloc: std.mem.Allocator, path: []const u8, inode: Inode, parent: ?*Atomic(usize), origin: bool) Error!FileReturn { - std.debug.print("started extract of {s}\n", .{path}); - defer { - if (parent != null) - _ = parent.?.fetchSub(1, .acquire); - if (!origin) inode.deinit(alloc); - } - errdefer if (!origin) alloc.free(path); - - var ret: FileReturn = .{ +fn extractNod(alloc: std.mem.Allocator, path: []const u8, inode: Inode) Error!FileReturn(false) { + var ret: FileReturn(false) = .{ + .hdr = inode.hdr, .path = path, - .origin = origin, - - .uid_idx = inode.hdr.uid_idx, - .gid_idx = inode.hdr.gid_idx, - .permissions = inode.hdr.permission, - .mod_time = inode.hdr.mod_time, }; const DT = std.os.linux.DT; @@ -324,133 +234,92 @@ fn extractNod(alloc: std.mem.Allocator, path: []const u8, inode: Inode, parent: return ret; } -// Loop +fn setMetadata(alloc: std.mem.Allocator, io: Io, id_table: *Lookup.Table(u16), xattr_table: *XattrTable, options: ExtractionOptions, hdr: Inode.Header, xattr_idx: ?u32, path: []const u8) Error!void { + if (options.ignore_permissions and (options.ignore_xattr or xattr_idx == null)) return; -fn returnLoop(alloc: std.mem.Allocator, io: Io, id_table: *Lookup.Table(u16), xattr_table: *XattrTable, sel: *Io.Select(ReturnUnion), options: ExtractionOptions) !void { - var origin_finished = false; - while (!origin_finished) { - const finished = try sel.await(); + const file = try Io.Dir.cwd().openFile(io, path, .{}); + defer file.close(io); - switch (finished) { - .dir_ret => |d| { - const ret = try d; - if (ret.sub_files.load(.unordered) != 0) { - sel.queue.putOne(io, .{ .dir_ret = ret }) catch |err| { - if (!ret.origin) alloc.free(ret.path); - alloc.destroy(ret.sub_files); - return err; - }; - continue; - } - defer { - if (!ret.origin) { - alloc.free(ret.path); - } else { - origin_finished = true; - } - } - defer std.debug.print("finished extract of {s}\n", .{ret.path}); - alloc.destroy(ret.sub_files); + if (!options.ignore_permissions) { + try file.setTimestamps(io, .{ .modify_timestamp = .init(Io.Timestamp.fromNanoseconds(@as(i96, @intCast(hdr.mod_time)) * std.time.ns_per_s)) }); + try file.setPermissions(io, @enumFromInt(hdr.permission)); + try file.setOwner(io, try id_table.get(io, hdr.uid_idx), try id_table.get(io, hdr.gid_idx)); + } - if (!options.ignore_permissions and (!options.ignore_xattr and ret.xattr_idx != null)) { - const file = try Io.Dir.cwd().openFile(io, ret.path, .{}); - defer file.close(io); + if (!options.ignore_permissions and xattr_idx != null) { + var xattrs = try xattr_table.get(alloc, io, xattr_idx.?); + defer xattrs.deinit(alloc); - if (!options.ignore_permissions) { - try file.setTimestamps(io, .{ - .modify_timestamp = .init(.{ .nanoseconds = @as(i96, @intCast(ret.mod_time)) * std.time.ns_per_s }), - }); - try file.setPermissions(io, @enumFromInt(ret.permissions)); - try file.setOwner(io, try id_table.get(io, ret.uid_idx), try id_table.get(io, ret.gid_idx)); - } - if (!options.ignore_xattr and ret.xattr_idx != null) { - const xattrs = xattr_table.get(alloc, io, ret.xattr_idx.?) catch |err| { - std.debug.print("YO {}\n", .{err}); - return err; - }; - defer xattrs.deinit(alloc); - - for (xattrs.xattrs) |x| { - const res = std.os.linux.fsetxattr(file.handle, x.key, x.value.ptr, x.value.len, 0); - if (res != 0) - return error.SetXattrFailed; - } - } - } - }, - .file_ret => |f| { - const ret = try f; - defer { - if (!ret.origin) { - alloc.free(ret.path); - } else { - origin_finished = true; - } - } - defer std.debug.print("finished extract of {s}\n", .{ret.path}); - - if (!options.ignore_permissions and (!options.ignore_xattr and ret.xattr_idx != null)) { - const file = try Io.Dir.cwd().openFile(io, ret.path, .{}); - defer file.close(io); - - if (!options.ignore_permissions) { - try file.setTimestamps(io, .{ - .modify_timestamp = .init(.{ .nanoseconds = @as(i96, @intCast(ret.mod_time)) * std.time.ns_per_s }), - }); - try file.setPermissions(io, @enumFromInt(ret.permissions)); - try file.setOwner(io, try id_table.get(io, ret.uid_idx), try id_table.get(io, ret.gid_idx)); - } - if (!options.ignore_xattr and ret.xattr_idx != null) { - const xattrs = xattr_table.get(alloc, io, ret.xattr_idx.?) catch |err| { - std.debug.print("YO!!! {s} {}\n", .{ ret.path, err }); - return err; - }; - defer xattrs.deinit(alloc); - - for (xattrs.xattrs) |x| { - const res = std.os.linux.fsetxattr(file.handle, x.key, x.value.ptr, x.value.len, 0); - if (res != 0) - return error.SetXattrFailed; - } - } - } - }, - .void_ret => |v| try v, + for (xattrs.xattrs) |x| { + const res = std.os.linux.fsetxattr(file.handle, x.key, x.value.ptr, x.value.len, 0); + if (res != 0) + return Error.SetXattrError; } } } -// Utility types - -const ReturnUnion = union(enum) { - file_ret: Error!FileReturn, - dir_ret: Error!DirReturn, - void_ret: Error!void, -}; - -const Error = error{ Canceled, MknodError } || Directory.Error || Io.Dir.CreateFileAtomicError || Io.File.Atomic.LinkError || +const Error = error{ Canceled, MknodError, SetXattrError } || Directory.Error || Io.Dir.CreateFileAtomicError || Io.File.Atomic.LinkError || DataExtractor.Error || Io.Dir.SymLinkError || Io.Dir.CreateDirPathError; -const FileReturn = struct { - path: []const u8, - origin: bool, - - uid_idx: u32, - gid_idx: u32, - mod_time: u32, - permissions: u16, - - xattr_idx: ?u32 = null, +const ReturnUnion = union(enum) { + file: Error!FileReturn, + dir: Error!DirReturn(true), + void: Error!void, }; -const DirReturn = struct { - path: []const u8, - sub_files: *Atomic(usize), - origin: bool, - uid_idx: u32, - gid_idx: u32, - mod_time: u32, - permissions: u16, +fn FileReturn(comptime async: bool) type { + return if (async) + struct { + hdr: Inode.Header, + path: []const u8, + parent: ?*Atomic(u32), - xattr_idx: ?u32 = null, -}; + xattr_idx: ?u32 = null, + + fn finish(self: FileReturn(async), alloc: std.mem.Allocator, io: Io, id_table: *Lookup.Table(u16), xattr_table: *XattrTable, options: ExtractionOptions) Error!void { + defer if (self.parent != null) { + _ = self.parent.?.fetchSub(1, .acquire); + }; + return setMetadata(alloc, io, id_table, xattr_table, options, self.hdr, self.xattr_idx, self.path); + } + } + else + struct { + hdr: Inode.Header, + path: []const u8, + + xattr_idx: ?u32 = null, + + fn finish(self: FileReturn(async), alloc: std.mem.Allocator, io: Io, id_table: *Lookup.Table(u16), xattr_table: *XattrTable, options: ExtractionOptions) Error!void { + return setMetadata(alloc, io, id_table, xattr_table, options, self.hdr, self.xattr_idx, self.path); + } + }; +} +fn DirReturn(comptime async: bool) type { + return if (async) + struct { + hdr: Inode.Header, + path: []const u8, + parent: ?*Atomic(u32), + + xattr_idx: ?u32 = null, + + fn finish(self: DirReturn(async), alloc: std.mem.Allocator, io: Io, id_table: *Lookup.Table(u16), xattr_table: *XattrTable, options: ExtractionOptions) Error!void { + defer if (self.parent != null) { + _ = self.parent.?.fetchSub(1, .acquire); + }; + return setMetadata(alloc, io, id_table, xattr_table, options, self.hdr, self.xattr_idx, self.path); + } + } + else + struct { + hdr: Inode.Header, + path: []const u8, + + xattr_idx: ?u32 = null, + + fn finish(self: DirReturn(async), alloc: std.mem.Allocator, io: Io, id_table: *Lookup.Table(u16), xattr_table: *XattrTable, options: ExtractionOptions) Error!void { + return setMetadata(alloc, io, id_table, xattr_table, options, self.hdr, self.xattr_idx, self.path); + } + }; +} diff --git a/src/xattr.zig b/src/xattr.zig index 3ff467f..40b3292 100644 --- a/src/xattr.zig +++ b/src/xattr.zig @@ -4,6 +4,7 @@ const Io = std.Io; const DecompCache = @import("decomp_cache.zig"); const InodeRef = @import("inode.zig").Ref; const MetadataReader = @import("meta_rdr.zig"); + const XattrEntryTable = @import("lookup.zig").Table(XattrEntry); const XattrTable = @This(); @@ -21,8 +22,8 @@ pub fn init(alloc: std.mem.Allocator, cache: *DecompCache, xattr_start: u64) !Xa .table = .init(alloc, cache, xattr_start + 16, num), }; } -pub fn deinit(self: *XattrTable) void { - self.table.deinit(); +pub fn deinit(self: *XattrTable, io: Io) void { + self.table.deinit(io); } pub fn get(self: *XattrTable, alloc: std.mem.Allocator, io: Io, idx: u32) !XattrKVs {