Nearly there...

Various tweaks & fixes to get extraction working properly
This commit is contained in:
Caleb Gardner
2025-05-27 14:21:40 -05:00
parent 985e2bd7e5
commit 17dbda3326
7 changed files with 96 additions and 28 deletions
+1
View File
@@ -27,6 +27,7 @@ pub const DirEntry = struct {
fn init(alloc: std.mem.Allocator, hdr: DirHeader, rdr: io.AnyReader) !DirEntry { fn init(alloc: std.mem.Allocator, hdr: DirHeader, rdr: io.AnyReader) !DirEntry {
const raw = try rdr.readStruct(RawDirEntryStart); const raw = try rdr.readStruct(RawDirEntryStart);
const name = try alloc.alloc(u8, raw.name_size + 1); const name = try alloc.alloc(u8, raw.name_size + 1);
errdefer alloc.free(name);
_ = try rdr.read(name); _ = try rdr.read(name);
return .{ return .{
.block_start = hdr.inode_block_start, .block_start = hdr.inode_block_start,
+13 -12
View File
@@ -113,6 +113,7 @@ pub const File = struct {
} }
try self.readDirEntries(rdr); try self.readDirEntries(rdr);
var files = try rdr.alloc.alloc(File, self.dirEntries.?.count()); var files = try rdr.alloc.alloc(File, self.dirEntries.?.count());
errdefer rdr.alloc.free(files);
var dirEntryIter = self.dirEntries.?.valueIterator(); var dirEntryIter = self.dirEntries.?.valueIterator();
var i: u32 = 0; var i: u32 = 0;
while (dirEntryIter.next()) |ent| : (i += 1) { while (dirEntryIter.next()) |ent| : (i += 1) {
@@ -208,7 +209,7 @@ pub const File = struct {
pub fn extract(self: *File, rdr: *Reader, config: ExtractConfig, path: []const u8) (ExtractError || anyerror)!void { pub fn extract(self: *File, rdr: *Reader, config: ExtractConfig, path: []const u8) (ExtractError || anyerror)!void {
var pol: std.Thread.Pool = undefined; var pol: std.Thread.Pool = undefined;
try pol.init(.{ try pol.init(.{
.allocator = std.heap.smp_allocator, .allocator = rdr.alloc,
.n_jobs = config.thread_count, .n_jobs = config.thread_count,
}); });
defer pol.deinit(); defer pol.deinit();
@@ -233,22 +234,22 @@ pub const File = struct {
} }
var iter = try self.iterator(rdr); var iter = try self.iterator(rdr);
defer iter.deinit(); defer iter.deinit();
while (iter.next()) |*f| { while (iter.next()) |f| {
const extr_path = try std.mem.concat(rdr.alloc, u8, &[3][]const u8{ real_path, "/", f.name }); const extr_path = try std.mem.concat(rdr.alloc, u8, &[3][]const u8{ real_path, "/", f.name });
defer rdr.alloc.free(extr_path); defer rdr.alloc.free(extr_path);
try @constCast(f).extractReal(rdr, config, pool, extr_path, false); try f.extractReal(rdr, config, pool, extr_path, false);
} }
}, },
.file, .ext_file => { .file, .ext_file => {
if ((!first and exists) or if ((!first and exists) or
(first and exists and stat.?.kind != .directory)) return ExtractError.FileExists; (first and exists and stat.?.kind != .directory)) return ExtractError.FileExists;
const extr_path = if (first and exists and stat.?.kind == .directory) blk: { var extr_path: []u8 = undefined;
break :blk try std.mem.concat(rdr.alloc, u8, &[3][]const u8{ real_path, "/", self.name }); if (first and exists and stat.?.kind == .directory) {
} else blk: { extr_path = try std.mem.concat(rdr.alloc, u8, &[3][]const u8{ real_path, "/", self.name });
const tmp = try rdr.alloc.alloc(u8, real_path.len); } else {
@memcpy(tmp, real_path); extr_path = try rdr.alloc.alloc(u8, real_path.len);
break :blk tmp; @memcpy(extr_path, real_path);
}; }
defer rdr.alloc.free(extr_path); defer rdr.alloc.free(extr_path);
var ext = try self.extractor(rdr); var ext = try self.extractor(rdr);
defer ext.deinit(); defer ext.deinit();
@@ -289,10 +290,10 @@ const FileIterator = struct {
curIndex: u32 = 0, curIndex: u32 = 0,
pub fn next(self: *FileIterator) ?File { pub fn next(self: *FileIterator) ?*File {
if (self.curIndex >= self.files.len) return null; if (self.curIndex >= self.files.len) return null;
defer self.curIndex += 1; defer self.curIndex += 1;
return self.files[self.curIndex]; return &self.files[self.curIndex];
} }
pub fn reset(self: *FileIterator) void { pub fn reset(self: *FileIterator) void {
self.curIndex = 0; self.curIndex = 0;
+40 -1
View File
@@ -54,10 +54,17 @@ pub const Reader = struct {
return out; return out;
} }
pub fn deinit(self: *Reader) void { pub fn deinit(self: *Reader) void {
self.frag_table.deinit(self.alloc);
self.export_table.deinit(self.alloc);
self.id_table.deinit(self.alloc);
self.root.deinit(self.alloc); self.root.deinit(self.alloc);
self.holder.deinit(); self.holder.deinit();
} }
pub fn open(self: *Reader, path: []const u8) !File {
return self.root.open(self, path);
}
fn fileFromRef(self: *Reader, ref: inode.InodeRef, name: []const u8) !File { fn fileFromRef(self: *Reader, ref: inode.InodeRef, name: []const u8) !File {
var offset_rdr = self.holder.readerAt(ref.block_start + self.super.inode_table_start); var offset_rdr = self.holder.readerAt(ref.block_start + self.super.inode_table_start);
var meta_rdr: MetadataReader = .init( var meta_rdr: MetadataReader = .init(
@@ -92,8 +99,40 @@ test "root iter" {
test "extract" { test "extract" {
const test_sfs_path = "testing/LinuxPATest.sfs"; const test_sfs_path = "testing/LinuxPATest.sfs";
const extract_path = "testing/testExtract"; const extract_path = "testing/testExtract";
try std.fs.cwd().deleteTree(extract_path); std.fs.cwd().deleteTree(extract_path) catch |err| {
if (err != std.fs.Dir.DeleteFileError.FileNotFound) {
return err;
}
};
var rdr: Reader = try .init(std.testing.allocator, test_sfs_path, 0); var rdr: Reader = try .init(std.testing.allocator, test_sfs_path, 0);
defer rdr.deinit(); defer rdr.deinit();
try rdr.root.extract(&rdr, try .init(), extract_path); try rdr.root.extract(&rdr, try .init(), extract_path);
} }
test "extract single file" {
const test_sfs_path = "testing/LinuxPATest.sfs";
const sfs_file_path = "Start.exe";
const extract_path = "testing/Start.exe";
std.fs.cwd().deleteFile(extract_path) catch |err| {
if (err != std.fs.Dir.DeleteFileError.FileNotFound) {
return err;
}
};
var rdr: Reader = try .init(std.testing.allocator, test_sfs_path, 0);
defer rdr.deinit();
var fil = try rdr.open(sfs_file_path);
defer fil.deinit(std.testing.allocator);
try fil.extract(&rdr, try .init(), extract_path);
}
test "extract single directory" {
const test_sfs_path = "testing/LinuxPATest.sfs";
const sfs_file_path = "Documents";
const extract_path = "testing/Documents";
try std.fs.cwd().deleteTree(extract_path);
var rdr: Reader = try .init(std.testing.allocator, test_sfs_path, 0);
defer rdr.deinit();
var fil = try rdr.open(sfs_file_path);
defer fil.deinit(std.testing.allocator);
try fil.extract(&rdr, try .init(), extract_path);
}
+18 -2
View File
@@ -79,7 +79,8 @@ pub const DataExtractor = struct {
if (self.frag_data != null) self.alloc.free(self.frag_data.?); if (self.frag_data != null) self.alloc.free(self.frag_data.?);
} }
fn processBlockToFile(self: *DataExtractor, errs: *std.ArrayList(anyerror), block_ind: usize, fil: *fs.File) void { fn processBlockToFile(self: *DataExtractor, wg: *std.Thread.WaitGroup, errs: *std.ArrayList(anyerror), block_ind: usize, fil: *fs.File) void {
defer wg.finish();
const offset_rdr = self.holder.readerAt(self.block_offset[block_ind]); const offset_rdr = self.holder.readerAt(self.block_offset[block_ind]);
var fil_wrtr: FileOffsetWriter = .init(fil, block_ind * self.block_size); var fil_wrtr: FileOffsetWriter = .init(fil, block_ind * self.block_size);
var limit = std.io.limitedReader(offset_rdr, self.sizes[block_ind].size); var limit = std.io.limitedReader(offset_rdr, self.sizes[block_ind].size);
@@ -94,6 +95,15 @@ pub const DataExtractor = struct {
}; };
} }
fn fragmentToFile(self: *DataExtractor, wg: *std.Thread.WaitGroup, errs: *std.ArrayList(anyerror), fil: *fs.File) void {
defer wg.finish();
fil.pwriteAll(self.frag_data.?, self.block_size * self.sizes.len) catch |err| {
errs.append(err) catch |ignored_err| {
std.debug.print("{}\n", .{ignored_err});
};
};
}
/// Write the data completely to the given file. /// Write the data completely to the given file.
/// Ignores the file's current offset and writes from the beginning of the file. /// Ignores the file's current offset and writes from the beginning of the file.
/// Returns the amount of bytes written. /// Returns the amount of bytes written.
@@ -104,9 +114,15 @@ pub const DataExtractor = struct {
var errs: std.ArrayList(anyerror) = .init(self.alloc); var errs: std.ArrayList(anyerror) = .init(self.alloc);
defer errs.deinit(); defer errs.deinit();
for (0..self.sizes.len) |i| { for (0..self.sizes.len) |i| {
pool.spawnWg(&wg, processBlockToFile, .{ self, &errs, i, fil }); wg.start();
try pool.spawn(processBlockToFile, .{ self, &wg, &errs, i, fil });
}
if (self.frag_data != null) {
wg.start();
try pool.spawn(fragmentToFile, .{ self, &wg, &errs, fil });
} }
wg.wait(); wg.wait();
//TODO: see if there's any errors
} }
// fn processBlock(self: *DataExtractor, errs: std.ArrayList(anyerror), data_out: std.AutoHashMap([]u8), block_ind: u32) void { // fn processBlock(self: *DataExtractor, errs: std.ArrayList(anyerror), data_out: std.AutoHashMap([]u8), block_ind: u32) void {
+1 -1
View File
@@ -64,7 +64,7 @@ pub const MetadataReader = struct {
while (cur_read < bytes.len) { while (cur_read < bytes.len) {
if (self.offset >= self.block.len) try self.readNextBlock(); if (self.offset >= self.block.len) try self.readNextBlock();
to_read = @min(bytes.len - cur_read, self.block.len - self.offset); to_read = @min(bytes.len - cur_read, self.block.len - self.offset);
@memcpy(bytes[cur_read..], self.block[self.offset .. @as(usize, self.offset) + to_read]); @memcpy(bytes[cur_read .. cur_read + to_read], self.block[self.offset .. @as(usize, self.offset) + to_read]);
self.offset += @truncate(to_read); self.offset += @truncate(to_read);
cur_read += to_read; cur_read += to_read;
} }
+4 -5
View File
@@ -26,22 +26,21 @@ pub fn Table(
}; };
} }
pub fn deinit(self: *Self, alloc: std.mem.Allocator) void { pub fn deinit(self: *Self, alloc: std.mem.Allocator) void {
if (self.table.len == 0) alloc.free(self.table); if (self.table.len != 0) alloc.free(self.table);
} }
pub fn getValue(self: *Self, read: *Reader, i: u64) !T { pub fn getValue(self: *Self, read: *Reader, i: u64) !T {
if (i >= self.item_count) return TableError.InvalidIndex; if (i >= self.item_count) return TableError.InvalidIndex;
if (self.table.len > i) return self.table[i]; if (self.table.len > i) return self.table[i];
var meta_rdr: MetadataReader = undefined;
var offset_rdr: FileOffsetReader = undefined; var offset_rdr: FileOffsetReader = undefined;
var meta_rdr: MetadataReader = undefined;
var meta_buf: [8]u8 = [1]u8{0} ** 8; var meta_buf: [8]u8 = [1]u8{0} ** 8;
var meta_offset: u64 = 0; const meta_offset = std.mem.bytesAsValue(u64, &meta_buf);
var to_read: u32 = 0; var to_read: u32 = 0;
while (self.table.len <= i) { while (self.table.len <= i) {
_ = try read.holder.file.preadAll(&meta_buf, self.offset); _ = try read.holder.file.preadAll(&meta_buf, self.offset);
self.offset += 8; self.offset += 8;
meta_offset = std.mem.bytesToValue(u8, &meta_buf); offset_rdr = read.holder.readerAt(meta_offset.*);
offset_rdr = read.holder.readerAt(meta_offset);
meta_rdr = .init(read.alloc, self.decomp, offset_rdr.any()); meta_rdr = .init(read.alloc, self.decomp, offset_rdr.any());
defer meta_rdr.deinit(); defer meta_rdr.deinit();
to_read = @min(self.item_count - self.table.len, comptime 8192 / @sizeOf(T)); to_read = @min(self.item_count - self.table.len, comptime 8192 / @sizeOf(T));
+19 -7
View File
@@ -2,6 +2,7 @@ const std = @import("std");
const config = @import("config"); const config = @import("config");
const Reader = @import("reader.zig").Reader; const Reader = @import("reader.zig").Reader;
const ExtractConfig = @import("file.zig").File.ExtractConfig;
const stdout = std.io.getStdOut(); const stdout = std.io.getStdOut();
@@ -121,26 +122,37 @@ pub fn main() !void {
_ = try stdout.writeAll("no extract location given\n"); _ = try stdout.writeAll("no extract location given\n");
return; return;
} }
var rdr: Reader = .init( var rdr = Reader.init(
alloc.allocator(), alloc.allocator(),
filename, filename,
offset, offset,
) catch |err| { ) catch |err| {
try std.fmt.format(stdout.writer(), "Error opening {s} as squashfs: {any}", "\n", .{ filename, err }); try std.fmt.format(stdout.writer(), "Error opening {s} as squashfs: {any}\n", .{ filename, err });
return;
}; };
if (list == .None) { if (list == .None) {
var conf = ExtractConfig.init() catch |err| {
try std.fmt.format(stdout.writer(), "Error getting system info: {any}\n", .{err});
return;
};
conf.deref_sym = deref;
conf.unbreak_sym = unbreak;
conf.verbose = verbose;
if (extr_files.items.len == 0) { if (extr_files.items.len == 0) {
rdr.root.extract(&rdr, extr_location) catch |err| { rdr.root.extract(&rdr, conf, extr_location) catch |err| {
try std.fmt.format(stdout.writer(), "Error extracting archive: {any}", "\n", .{err}); try std.fmt.format(stdout.writer(), "Error extracting archive: {any}\n", .{err});
return;
}; };
} else { } else {
for (extr_files.items) |path| { for (extr_files.items) |path| {
var fil = rdr.root.open(&rdr, path) catch |err| { var fil = rdr.root.open(&rdr, path) catch |err| {
try std.fmt.format(stdout.writer(), "Error extracting {s}: {any}", "\n", .{ path, err }); try std.fmt.format(stdout.writer(), "Error extracting {s}: {any}\n", .{ path, err });
return;
}; };
defer fil.deinit(alloc.allocator()); defer fil.deinit(alloc.allocator());
fil.extract(&rdr, extr_location) catch |err| { fil.extract(&rdr, conf, extr_location) catch |err| {
try std.fmt.format(stdout.writer(), "Error extracting {s}: {any}", "\n", .{ path, err }); try std.fmt.format(stdout.writer(), "Error extracting {s}: {any}\n", .{ path, err });
return;
}; };
} }
} }