From a96ad46a6c160ba7094fb72ed2a8aece58a7ddb5 Mon Sep 17 00:00:00 2001 From: Caleb Gardner Date: Tue, 22 Jul 2025 05:13:34 -0500 Subject: [PATCH] Things are working (though slowly) --- build.zig | 2 +- src/bin/unsquashfs.zig | 24 ++++- src/file.zig | 217 ++++++++++++++++++++++++++++++++--------- src/reader/data.zig | 14 ++- src/root.zig | 8 +- 5 files changed, 207 insertions(+), 58 deletions(-) diff --git a/build.zig b/build.zig index c682bc7..34e9b29 100644 --- a/build.zig +++ b/build.zig @@ -31,7 +31,7 @@ pub fn build(b: *std.Build) !void { exe_mod.addOptions("config", opt); const exe = b.addExecutable(.{ .linkage = .static, - .name = "zig-unsquashfs", + .name = "unsquashfs", .root_module = exe_mod, .version = sem_ver, }); diff --git a/src/bin/unsquashfs.zig b/src/bin/unsquashfs.zig index ceca2e0..7727b15 100644 --- a/src/bin/unsquashfs.zig +++ b/src/bin/unsquashfs.zig @@ -51,10 +51,10 @@ const ListTypes = enum { }; pub fn main() !void { - var alloc: std.heap.GeneralPurposeAllocator(.{}) = .init; - extr_files = .init(alloc.allocator()); + const alloc = std.heap.smp_allocator; + extr_files = .init(alloc); defer extr_files.deinit(); - var args = std.process.argsWithAllocator(alloc.allocator()) catch { + var args = std.process.argsWithAllocator(alloc) catch { _ = try stdout.writeAll("Unable to allocate memory"); return; }; @@ -119,8 +119,10 @@ pub fn main() !void { return; } const fil = try std.fs.cwd().openFile(filename, .{}); + defer fil.close(); + var th_alloc: std.heap.ThreadSafeAllocator = .{ .child_allocator = std.heap.smp_allocator }; var rdr = squashfs.SfsFile.init( - alloc.allocator(), + th_alloc.allocator(), fil, offset, ) catch |err| { @@ -128,5 +130,17 @@ pub fn main() !void { return; }; defer rdr.deinit(); - //TODO + //TODO: list and extr_files; + var op: squashfs.ExtractionOptions = squashfs.ExtractionOptions.init() catch |err| { + try std.fmt.format(stdout.writer(), "Error setting extraction options: {any}\n", .{err}); + return; + }; + op.verbose = verbose; + op.dereference_symlinks = deref; + op.unbreak_symlinks = unbreak; + if (processors != 0) op.thread_count = processors; + rdr.extract(op, extr_location) catch |err| { + try std.fmt.format(stdout.writer(), "Error extracting archive: {any}\n", .{err}); + return; + }; } diff --git a/src/file.zig b/src/file.zig index 4c637e6..8c18f86 100644 --- a/src/file.zig +++ b/src/file.zig @@ -219,7 +219,7 @@ pub fn File(comptime T: type) type { defer pol.deinit(); var errs: std.ArrayList(anyerror) = .init(self.rdr.alloc); defer errs.deinit(); - try self.extractReal(op, path, &errs, &wg, &pol, true); + self.extractReal(op, path, &errs, &wg, &pol, true); wg.wait(); if (errs.items.len > 0) return errs.items[0]; } @@ -231,7 +231,7 @@ pub fn File(comptime T: type) type { wg: *WaitGroup, pol: *Pool, first: bool, - ) !void { + ) void { if (errs.items.len > 0) return; if (op.verbose) { std.fmt.format( @@ -242,18 +242,22 @@ pub fn File(comptime T: type) type { } return switch (self.inode.hdr.type) { .dir, .ext_dir => { + var complete = false; wg.start(); - defer std.debug.print("{}\n", .{wg.state.raw}); - errdefer wg.finish(); + defer if (!complete) wg.finish(); std.fs.cwd().makeDir(path) catch |err| { if (err != std.fs.Dir.MakeError.PathAlreadyExists) { - return err; + errs.append(err) catch {}; + return; } }; - var dir_wg = try self.rdr.alloc.create(WaitGroup); + const dir_wg = self.rdr.alloc.create(WaitGroup) catch |err| { + errs.append(err) catch {}; + return; + }; dir_wg.* = .{}; for (self.entries.?) |ent| { - var fil = initFromEntry(self.rdr, ent) catch |err| { + const fil = initFromEntry(self.rdr, ent) catch |err| { std.fmt.format( op.verbose_logger, "error extracting inode {} \"{s}\": {}\n", @@ -285,39 +289,109 @@ pub fn File(comptime T: type) type { ) catch {}; continue; }; - fil.extractReal(op, ext_path, errs, dir_wg, pol, false) catch |err| { - std.fmt.format( - op.verbose_logger, - "error extracting inode {} \"{s}\": {}\n", - .{ ent.num, path, err }, - ) catch {}; + var thr = std.Thread.spawn(.{ .allocator = self.rdr.alloc }, extractReal, .{ + fil, + op, + ext_path, + errs, + dir_wg, + pol, + false, + }) catch |err| { + self.rdr.alloc.free(ext_path); + if (op.verbose) { + std.fmt.format( + op.verbose_logger, + "error extracting inode {} \"{s}\": {}\n", + .{ ent.num, path, err }, + ) catch {}; + } continue; }; + thr.detach(); } - dir_wg.wait(); - wg.finish(); - std.debug.print("finished: {s}\n", .{path}); + var thr = std.Thread.spawn( + .{ .allocator = self.rdr.alloc }, + extractDirWait, + .{ + self, + op, + path, + dir_wg, + wg, + first, + }, + ) catch |err| { + if (op.verbose) { + std.fmt.format( + op.verbose_logger, + "error spawning wait thread for \"{s}\": {}\n", + .{ path, err }, + ) catch {}; + } + self.extractDirWait(op, path, dir_wg, wg, first); + return; + }; + thr.detach(); + complete = true; }, .file, .ext_file => { + var complete = false; wg.start(); - errdefer wg.finish(); - var ext_fil = try std.fs.cwd().createFile(path, .{}); - errdefer ext_fil.close(); - var fil_errs = try self.rdr.alloc.create(std.ArrayList(anyerror)); - errdefer self.rdr.alloc.destroy(fil_errs); + defer if (!complete) wg.finish(); + var ext_fil = std.fs.cwd().createFile(path, .{}) catch |err| { + if (op.verbose) { + std.fmt.format( + op.verbose_logger, + "error creating file \"{s}\": {}\n", + .{ path, err }, + ) catch {}; + } + errs.append(err) catch {}; + return; + }; + defer if (!complete) ext_fil.close(); + var fil_errs = self.rdr.alloc.create(std.ArrayList(anyerror)) catch |err| { + if (op.verbose) { + std.fmt.format( + op.verbose_logger, + "error allocating memory: {}\n", + .{err}, + ) catch {}; + } + errs.append(err) catch {}; + return; + }; + defer if (!complete) self.rdr.alloc.destroy(fil_errs); fil_errs.* = .init(self.rdr.alloc); - errdefer fil_errs.deinit(); + defer if (!complete) fil_errs.deinit(); @constCast(&self.data_reader.?).setPool(pol); - try self.data_reader.?.writeToNoBlock(errs, ext_fil, extractRegFinish, .{ - self, - op, - path, - fil_errs, + self.data_reader.?.writeToNoBlock( errs, - wg, ext_fil, - first, - }); + extractRegFinish, + .{ + self, + op, + path, + fil_errs, + errs, + wg, + ext_fil, + first, + }, + ) catch |err| { + if (op.verbose) { + std.fmt.format( + op.verbose_logger, + "error extracting file \"{s}\": {}\n", + .{ path, err }, + ) catch {}; + } + errs.append(err) catch {}; + return; + }; + complete = true; }, .symlink, .ext_symlink => {}, .block_dev, .ext_block_dev, .char_dev, .ext_char_dev, .fifo, .ext_fifo => { @@ -334,14 +408,66 @@ pub fn File(comptime T: type) type { }, }; } - // fn extractFileFinish( - // self: Self, - // op: ExtractionOptions, - // path: []const u8, - // dir_wg: *WaitGroup, - // dir_wg_mut: *Mutex, - // wg: *WaitGroup, - // ) void {} + fn extractDirWait( + self: Self, + op: ExtractionOptions, + path: []const u8, + dir_wg: *WaitGroup, + wg: *WaitGroup, + first: bool, + ) void { + dir_wg.wait(); + self.rdr.alloc.destroy(dir_wg); + defer { + wg.finish(); + if (!first) { + self.rdr.alloc.free(path); + self.deinit(); + } + } + if (op.ignore_permissions) return; + const dir_uid = self.uid() catch |err| { + std.fmt.format( + op.verbose_logger, + "error getting uid for inode {} \"{s}\": {}\n", + .{ self.inode.hdr.num, path, err }, + ) catch {}; + return; + }; + const dir_gid = self.gid() catch |err| { + std.fmt.format( + op.verbose_logger, + "error getting gid for inode {} \"{s}\": {}\n", + .{ self.inode.hdr.num, path, err }, + ) catch {}; + return; + }; + var ext_dir = std.fs.cwd().openFile(path, .{}) catch |err| { + std.fmt.format( + op.verbose_logger, + "error setting owner & permissions for \"{s}\": {}\n", + .{ path, err }, + ) catch {}; + return; + }; + defer ext_dir.close(); + ext_dir.chmod(self.inode.hdr.perm) catch |err| { + std.fmt.format( + op.verbose_logger, + "error setting permissions for inode {} \"{s}\": {}\n", + .{ self.inode.hdr.num, path, err }, + ) catch {}; + return; + }; + ext_dir.chown(dir_uid, dir_gid) catch |err| { + std.fmt.format( + op.verbose_logger, + "error setting owner for inode {} \"{s}\": {}\n", + .{ self.inode.hdr.num, path, err }, + ) catch {}; + return; + }; + } fn extractRegFinish( self: Self, op: ExtractionOptions, @@ -352,12 +478,15 @@ pub fn File(comptime T: type) type { fil: std.fs.File, first: bool, ) void { - defer std.debug.print("{}\n", .{wg.state.raw}); - defer wg.finish(); - defer fil.close(); - defer self.rdr.alloc.destroy(fil_errs); - defer if (!first) self.deinit(); - defer if (!first) self.rdr.alloc.free(path); + defer { + wg.finish(); + fil.close(); + self.rdr.alloc.destroy(fil_errs); + if (!first) { + self.deinit(); + self.rdr.alloc.free(path); + } + } if (fil_errs.items.len > 0) { if (op.verbose) { std.fmt.format( diff --git a/src/reader/data.zig b/src/reader/data.zig index aa6e8ff..3002a00 100644 --- a/src/reader/data.zig +++ b/src/reader/data.zig @@ -188,6 +188,10 @@ pub fn DataReader(comptime T: type) type { on_finish_args: anytype, ) !void { if (self.pool == null) return DataReaderError.ThreadPoolNotSet; + if (self.numBlocks() == 0) { + @call(.auto, on_finish, on_finish_args); + return; + } var mut: std.Thread.Mutex = .{}; var cur_idx: usize = 0; var block_wg = try self.alloc.create(std.Thread.WaitGroup); @@ -200,7 +204,8 @@ pub fn DataReader(comptime T: type) type { } block_wg.startMany(self.numBlocks()); for (0..self.numBlocks()) |i| { - try self.pool.?.spawn( + var thr = try std.Thread.spawn( + .{ .allocator = self.alloc }, comptime blk: { if (std.meta.hasFn(@TypeOf(writer), "pwrite")) { break :blk noBlockThreadPWrite; @@ -215,6 +220,7 @@ pub fn DataReader(comptime T: type) type { } }, ); + thr.detach(); } } @@ -353,12 +359,12 @@ pub fn DataReader(comptime T: type) type { self.writeBlockToPWrite(errs, idx, writer); finish_mut.lock(); block_wg.finish(); + const isDone = block_wg.isDone(); defer { - const done = block_wg.isDone(); finish_mut.unlock(); - if (done) self.alloc.destroy(finish_mut); + if (isDone) self.alloc.destroy(finish_mut); } - if (block_wg.isDone()) { + if (isDone) { self.alloc.destroy(block_wg); @call(.auto, on_finish, on_finish_args); } diff --git a/src/root.zig b/src/root.zig index 35391c5..b120a1f 100644 --- a/src/root.zig +++ b/src/root.zig @@ -26,8 +26,8 @@ test "OpenFile" { } test "ExtractSingleFile" { - const single_file = "Start.exe"; - const single_file_extr_loc = "testing/Start.exe"; + const single_file = "PortableApps/Notepad++Portable/App/Notepad++/doLocalConf.xml"; + const single_file_extr_loc = "testing/doLocalConf.xml"; std.fs.cwd().deleteFile(single_file_extr_loc) catch {}; const sfs_fil = try std.fs.cwd().openFile(test_archive, .{}); @@ -49,7 +49,7 @@ test "ExtractAll" { defer sfs_fil.close(); var rdr: SfsFile = try .init(std.testing.allocator, sfs_fil, 0); defer rdr.deinit(); - var op: ExtractionOptions = try .init(); - op.verbose = true; + const op: ExtractionOptions = try .init(); + // op.verbose = true; try rdr.extract(op, extr_dir); }