Further progress on extraction

This commit is contained in:
Caleb Gardner
2025-07-21 06:40:18 -05:00
parent 8998d28253
commit d5c50b19f2
4 changed files with 109 additions and 27 deletions
+75 -14
View File
@@ -206,8 +206,6 @@ pub fn File(comptime T: type) type {
.dir, .ext_dir => { .dir, .ext_dir => {
if (exists and stat.?.kind != .directory) { if (exists and stat.?.kind != .directory) {
return ExtractError.FileExists; return ExtractError.FileExists;
} else if (!exists) {
try std.fs.cwd().makeDir(path);
} }
}, },
else => if (exists) return ExtractError.FileExists, else => if (exists) return ExtractError.FileExists,
@@ -236,31 +234,85 @@ pub fn File(comptime T: type) type {
) !void { ) !void {
if (errs.items.len > 0) return; if (errs.items.len > 0) return;
if (op.verbose) { 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) { return switch (self.inode.hdr.type) {
.dir, .ext_dir => { .dir, .ext_dir => {
wg.start(); wg.start();
defer std.debug.print("{}\n", .{wg.state.raw});
errdefer wg.finish(); 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| { for (self.entries.?) |ent| {
var fil = initFromEntry(self.rdr, ent) catch |err| { 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; continue;
}; };
} }
dir_wg.wait();
wg.finish();
std.debug.print("finished: {s}\n", .{path});
}, },
.file, .ext_file => { .file, .ext_file => {
wg.start(); wg.start();
errdefer wg.finish(); errdefer wg.finish();
var ext_fil = try std.fs.cwd().createFile(path, .{}); var ext_fil = try std.fs.cwd().createFile(path, .{});
errdefer ext_fil.close(); 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(); errdefer fil_errs.deinit();
@constCast(&self.data_reader.?).setPool(pol); @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, self,
op, op,
path, path,
&fil_errs, fil_errs,
errs, errs,
wg, wg,
ext_fil, 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, self: Self,
op: ExtractionOptions, op: ExtractionOptions,
path: []const u8, path: []const u8,
@@ -292,18 +352,19 @@ pub fn File(comptime T: type) type {
fil: std.fs.File, fil: std.fs.File,
first: bool, first: bool,
) void { ) void {
defer std.debug.print("{}\n", .{wg.state.raw});
defer wg.finish(); defer wg.finish();
defer fil.close(); defer fil.close();
defer self.rdr.alloc.destroy(fil_errs);
defer if (!first) self.deinit(); defer if (!first) self.deinit();
defer if (!first) self.rdr.alloc.free(path);
if (fil_errs.items.len > 0) { if (fil_errs.items.len > 0) {
if (op.verbose) { if (op.verbose) {
for (fil_errs.items) |err| { std.fmt.format(
std.fmt.format( op.verbose_logger,
op.verbose_logger, "error extracting inode {} to \"{s}\": {}\n",
"error extracting inode {} to \"{s}\": {}\n", .{ self.inode.hdr.num, path, fil_errs.items[0] },
.{ self.inode.hdr.num, path, err }, ) catch {};
) catch {};
}
} }
errs.append(fil_errs.items[0]) catch {}; errs.append(fil_errs.items[0]) catch {};
return; return;
+1 -1
View File
@@ -61,7 +61,7 @@ pub fn SfsReader(comptime T: type) type {
} }
/// Extract the entire archive to the given path & with the given options. /// Extract the entire archive to the given path & with the given options.
/// Equivelent to calling extract on the root File. /// 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(); var rt = try self.root();
defer rt.deinit(); defer rt.deinit();
return rt.extract(op, path); return rt.extract(op, path);
+13 -6
View File
@@ -190,8 +190,10 @@ pub fn DataReader(comptime T: type) type {
if (self.pool == null) return DataReaderError.ThreadPoolNotSet; if (self.pool == null) return DataReaderError.ThreadPoolNotSet;
var mut: std.Thread.Mutex = .{}; var mut: std.Thread.Mutex = .{};
var cur_idx: usize = 0; var cur_idx: usize = 0;
var block_wg: std.Thread.WaitGroup = .{}; var block_wg = try self.alloc.create(std.Thread.WaitGroup);
var finish_mut: std.Thread.Mutex = .{}; block_wg.* = .{};
const finish_mut = try self.alloc.create(std.Thread.Mutex);
finish_mut.* = .{};
var completed: ?std.AutoHashMap(usize, []u8) = null; var completed: ?std.AutoHashMap(usize, []u8) = null;
if (!comptime std.meta.hasFn(@TypeOf(writer), "pwrite")) { if (!comptime std.meta.hasFn(@TypeOf(writer), "pwrite")) {
completed = std.AutoHashMap(usize, []u8).init(self.alloc); completed = std.AutoHashMap(usize, []u8).init(self.alloc);
@@ -207,9 +209,9 @@ pub fn DataReader(comptime T: type) type {
}, },
blk: { blk: {
if (comptime std.meta.hasFn(@TypeOf(writer), "pwrite")) { 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 { } 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, writer: anytype,
) void { ) void {
if (errs.items.len > 0) return; 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; var pos = idx * self.block_size;
if (self.frag.len == 0 and idx == self.sizes.len - 1) { if (self.frag.len == 0 and idx == self.sizes.len - 1) {
pos += self.file_size % self.block_size; pos += self.file_size % self.block_size;
@@ -351,8 +353,13 @@ pub fn DataReader(comptime T: type) type {
self.writeBlockToPWrite(errs, idx, writer); self.writeBlockToPWrite(errs, idx, writer);
finish_mut.lock(); finish_mut.lock();
block_wg.finish(); 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()) { if (block_wg.isDone()) {
self.alloc.destroy(block_wg);
@call(.auto, on_finish, on_finish_args); @call(.auto, on_finish, on_finish_args);
} }
} }
+20 -6
View File
@@ -6,8 +6,6 @@ pub const ExtractionOptions = @import("extract_options.zig");
pub const SfsFile = SfsReader(std.fs.File); pub const SfsFile = SfsReader(std.fs.File);
const test_archive = "testing/LinuxPATest.sfs"; const test_archive = "testing/LinuxPATest.sfs";
const test_file = "Start.exe";
const file_extr_loc = "testing/Start.exe";
test "OpenFile" { test "OpenFile" {
const sfs_fil = try std.fs.cwd().openFile(test_archive, .{}); const sfs_fil = try std.fs.cwd().openFile(test_archive, .{});
@@ -27,15 +25,31 @@ test "OpenFile" {
} }
} }
test "ExtractFile" { test "ExtractSingleFile" {
std.fs.cwd().deleteFile(file_extr_loc) catch {}; 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, .{}); const sfs_fil = try std.fs.cwd().openFile(test_archive, .{});
defer sfs_fil.close(); defer sfs_fil.close();
var rdr: SfsFile = try .init(std.testing.allocator, sfs_fil, 0); var rdr: SfsFile = try .init(std.testing.allocator, sfs_fil, 0);
defer rdr.deinit(); defer rdr.deinit();
const fil = try rdr.open(test_file); const fil = try rdr.open(single_file);
defer fil.deinit(); defer fil.deinit();
var op: ExtractionOptions = try .init(); var op: ExtractionOptions = try .init();
op.verbose = true; 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);
} }