From d5c50b19f2870d4d1d6974520cdd9cd41db8b85b Mon Sep 17 00:00:00 2001 From: Caleb Gardner Date: Mon, 21 Jul 2025 06:40:18 -0500 Subject: [PATCH] Further progress on extraction --- src/file.zig | 89 ++++++++++++++++++++++++++++++++++++++------- src/reader.zig | 2 +- src/reader/data.zig | 19 +++++++--- src/root.zig | 26 ++++++++++--- 4 files changed, 109 insertions(+), 27 deletions(-) diff --git a/src/file.zig b/src/file.zig index e1914ab..4c637e6 100644 --- a/src/file.zig +++ b/src/file.zig @@ -206,8 +206,6 @@ pub fn File(comptime T: type) type { .dir, .ext_dir => { if (exists and stat.?.kind != .directory) { return ExtractError.FileExists; - } else if (!exists) { - try std.fs.cwd().makeDir(path); } }, else => if (exists) return ExtractError.FileExists, @@ -236,31 +234,85 @@ pub fn File(comptime T: type) type { ) !void { if (errs.items.len > 0) return; if (op.verbose) { - std.fmt.format(op.verbose_logger, "extracting inode {} \"{s}\" to {s}...\n", .{ self.inode.hdr.num, self.name, path }) catch {}; + std.fmt.format( + op.verbose_logger, + "extracting inode {} \"{s}\" to {s}...\n", + .{ self.inode.hdr.num, self.name, path }, + ) catch {}; } return switch (self.inode.hdr.type) { .dir, .ext_dir => { wg.start(); + defer std.debug.print("{}\n", .{wg.state.raw}); errdefer wg.finish(); + std.fs.cwd().makeDir(path) catch |err| { + if (err != std.fs.Dir.MakeError.PathAlreadyExists) { + return err; + } + }; + var dir_wg = try self.rdr.alloc.create(WaitGroup); + dir_wg.* = .{}; for (self.entries.?) |ent| { var fil = initFromEntry(self.rdr, ent) catch |err| { + std.fmt.format( + op.verbose_logger, + "error extracting inode {} \"{s}\": {}\n", + .{ ent.num, path, err }, + ) catch {}; + continue; + }; + const ext_path = blk: { + if (path[path.len - 1] == '/') { + var new = self.rdr.alloc.alloc(u8, path.len + ent.name.len) catch |err| { + break :blk err; + }; + @memcpy(new[0..path.len], path); + @memcpy(new[path.len..], ent.name); + break :blk new; + } + var new = self.rdr.alloc.alloc(u8, path.len + ent.name.len + 1) catch |err| { + break :blk err; + }; + @memcpy(new[0..path.len], path); + new[path.len] = '/'; + @memcpy(new[path.len + 1 ..], ent.name); + break :blk new; + } catch |err| { + std.fmt.format( + op.verbose_logger, + "error extracting inode {} \"{s}\": {}\n", + .{ ent.num, path, err }, + ) 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 {}; continue; }; } + dir_wg.wait(); + wg.finish(); + std.debug.print("finished: {s}\n", .{path}); }, .file, .ext_file => { wg.start(); errdefer wg.finish(); var ext_fil = try std.fs.cwd().createFile(path, .{}); errdefer ext_fil.close(); - var fil_errs: std.ArrayList(anyerror) = .init(self.rdr.alloc); + var fil_errs = try self.rdr.alloc.create(std.ArrayList(anyerror)); + errdefer self.rdr.alloc.destroy(fil_errs); + fil_errs.* = .init(self.rdr.alloc); errdefer fil_errs.deinit(); @constCast(&self.data_reader.?).setPool(pol); - try self.data_reader.?.writeToNoBlock(errs, ext_fil, filExtractFinish, .{ + try self.data_reader.?.writeToNoBlock(errs, ext_fil, extractRegFinish, .{ self, op, path, - &fil_errs, + fil_errs, errs, wg, ext_fil, @@ -282,7 +334,15 @@ pub fn File(comptime T: type) type { }, }; } - fn filExtractFinish( + // fn extractFileFinish( + // self: Self, + // op: ExtractionOptions, + // path: []const u8, + // dir_wg: *WaitGroup, + // dir_wg_mut: *Mutex, + // wg: *WaitGroup, + // ) void {} + fn extractRegFinish( self: Self, op: ExtractionOptions, path: []const u8, @@ -292,18 +352,19 @@ 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); if (fil_errs.items.len > 0) { if (op.verbose) { - for (fil_errs.items) |err| { - std.fmt.format( - op.verbose_logger, - "error extracting inode {} to \"{s}\": {}\n", - .{ self.inode.hdr.num, path, err }, - ) catch {}; - } + std.fmt.format( + op.verbose_logger, + "error extracting inode {} to \"{s}\": {}\n", + .{ self.inode.hdr.num, path, fil_errs.items[0] }, + ) catch {}; } errs.append(fil_errs.items[0]) catch {}; return; diff --git a/src/reader.zig b/src/reader.zig index 71e1096..1989ad3 100644 --- a/src/reader.zig +++ b/src/reader.zig @@ -61,7 +61,7 @@ pub fn SfsReader(comptime T: type) type { } /// Extract the entire archive to the given path & with the given options. /// Equivelent to calling extract on the root File. - pub fn extract(self: *Self, op: *ExtractionOptions, path: []const u8) !void { + pub fn extract(self: *Self, op: ExtractionOptions, path: []const u8) !void { var rt = try self.root(); defer rt.deinit(); return rt.extract(op, path); diff --git a/src/reader/data.zig b/src/reader/data.zig index f2521ac..aa6e8ff 100644 --- a/src/reader/data.zig +++ b/src/reader/data.zig @@ -190,8 +190,10 @@ pub fn DataReader(comptime T: type) type { if (self.pool == null) return DataReaderError.ThreadPoolNotSet; var mut: std.Thread.Mutex = .{}; var cur_idx: usize = 0; - var block_wg: std.Thread.WaitGroup = .{}; - var finish_mut: std.Thread.Mutex = .{}; + var block_wg = try self.alloc.create(std.Thread.WaitGroup); + block_wg.* = .{}; + const finish_mut = try self.alloc.create(std.Thread.Mutex); + finish_mut.* = .{}; var completed: ?std.AutoHashMap(usize, []u8) = null; if (!comptime std.meta.hasFn(@TypeOf(writer), "pwrite")) { completed = std.AutoHashMap(usize, []u8).init(self.alloc); @@ -207,9 +209,9 @@ pub fn DataReader(comptime T: type) type { }, blk: { if (comptime std.meta.hasFn(@TypeOf(writer), "pwrite")) { - break :blk .{ self, &block_wg, errs, i, writer, &finish_mut, on_finish, on_finish_args }; + break :blk .{ self, block_wg, errs, i, writer, finish_mut, on_finish, on_finish_args }; } else { - break :blk .{ self, &block_wg, &mut, &cur_idx, errs, &completed.?, i, writer, &finish_mut, on_finish, on_finish_args }; + break :blk .{ self, block_wg, &mut, &cur_idx, errs, &completed.?, i, writer, finish_mut, on_finish, on_finish_args }; } }, ); @@ -267,7 +269,7 @@ pub fn DataReader(comptime T: type) type { writer: anytype, ) void { if (errs.items.len > 0) return; - if (self.sizes[idx].size == 0) { + if (idx < self.sizes.len and self.sizes[idx].size == 0) { var pos = idx * self.block_size; if (self.frag.len == 0 and idx == self.sizes.len - 1) { pos += self.file_size % self.block_size; @@ -351,8 +353,13 @@ pub fn DataReader(comptime T: type) type { self.writeBlockToPWrite(errs, idx, writer); finish_mut.lock(); block_wg.finish(); - defer finish_mut.unlock(); + defer { + const done = block_wg.isDone(); + finish_mut.unlock(); + if (done) self.alloc.destroy(finish_mut); + } if (block_wg.isDone()) { + self.alloc.destroy(block_wg); @call(.auto, on_finish, on_finish_args); } } diff --git a/src/root.zig b/src/root.zig index df3b4b4..35391c5 100644 --- a/src/root.zig +++ b/src/root.zig @@ -6,8 +6,6 @@ pub const ExtractionOptions = @import("extract_options.zig"); pub const SfsFile = SfsReader(std.fs.File); const test_archive = "testing/LinuxPATest.sfs"; -const test_file = "Start.exe"; -const file_extr_loc = "testing/Start.exe"; test "OpenFile" { const sfs_fil = try std.fs.cwd().openFile(test_archive, .{}); @@ -27,15 +25,31 @@ test "OpenFile" { } } -test "ExtractFile" { - std.fs.cwd().deleteFile(file_extr_loc) catch {}; +test "ExtractSingleFile" { + const single_file = "Start.exe"; + const single_file_extr_loc = "testing/Start.exe"; + + std.fs.cwd().deleteFile(single_file_extr_loc) catch {}; const sfs_fil = try std.fs.cwd().openFile(test_archive, .{}); defer sfs_fil.close(); var rdr: SfsFile = try .init(std.testing.allocator, sfs_fil, 0); defer rdr.deinit(); - const fil = try rdr.open(test_file); + const fil = try rdr.open(single_file); defer fil.deinit(); var op: ExtractionOptions = try .init(); op.verbose = true; - try fil.extract(op, file_extr_loc); + try fil.extract(op, single_file_extr_loc); +} + +test "ExtractAll" { + const extr_dir = "testing/testExtract"; + + std.fs.cwd().deleteTree(extr_dir) catch {}; + const sfs_fil = try std.fs.cwd().openFile(test_archive, .{}); + 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; + try rdr.extract(op, extr_dir); }