Fixed some bugs; found some bugs

This commit is contained in:
Caleb Gardner
2026-06-03 06:28:25 -05:00
parent f17b402f08
commit dfd88fee13
9 changed files with 109 additions and 51 deletions
+4
View File
@@ -161,6 +161,8 @@ test "SingleFileExtraction" {
const alloc = std.testing.allocator; const alloc = std.testing.allocator;
const io = std.testing.io; const io = std.testing.io;
std.Io.Dir.cwd().deleteFile(io, TestFileExtractLocation) catch {};
var archive_file = try Io.Dir.cwd().openFile(io, TestArchive, .{}); var archive_file = try Io.Dir.cwd().openFile(io, TestArchive, .{});
defer archive_file.close(io); defer archive_file.close(io);
var arc: Archive = try .init(alloc, io, archive_file); var arc: Archive = try .init(alloc, io, archive_file);
@@ -178,6 +180,8 @@ test "FullExtraction" {
const alloc = std.testing.allocator; const alloc = std.testing.allocator;
const io = std.testing.io; const io = std.testing.io;
std.Io.Dir.cwd().deleteTree(io, TestFullExtractLocation) catch {};
var archive_file = try Io.Dir.cwd().openFile(io, TestArchive, .{}); var archive_file = try Io.Dir.cwd().openFile(io, TestArchive, .{});
defer archive_file.close(io); defer archive_file.close(io);
var arc: Archive = try .init(alloc, io, archive_file); var arc: Archive = try .init(alloc, io, archive_file);
+8 -4
View File
@@ -32,7 +32,7 @@ const help_mgs =
const errors = error{InvalidArguments}; const errors = error{InvalidArguments};
var archive: []const u8 = ""; var archive: []const u8 = "";
var extLoc: []const u8 = "squashfs-root"; var ext_loc: []const u8 = "squashfs-root";
var offset: u64 = 0; var offset: u64 = 0;
var threads: u32 = 0; var threads: u32 = 0;
var verbose: bool = false; var verbose: bool = false;
@@ -64,8 +64,12 @@ pub fn main(init: std.process.Init) !void {
.ignore_permissions = ignore_permissions, .ignore_permissions = ignore_permissions,
}; };
if (force) if (force)
try Io.Dir.cwd().deleteTree(io, extLoc); try Io.Dir.cwd().deleteTree(io, ext_loc);
try arc.extract(alloc, io, extLoc, options); //TODO: Handle error gracefully. if (threads != 1) {
try arc.extract(alloc, io, ext_loc, options); //TODO: Handle error gracefully.
} else {
try arc.extract(alloc, Io.Threaded.global_single_threaded.io(), ext_loc, options);
}
} }
fn handleArgs(args: std.process.Args, out: *Writer) !void { fn handleArgs(args: std.process.Args, out: *Writer) !void {
@@ -90,7 +94,7 @@ fn handleArgs(args: std.process.Args, out: *Writer) !void {
try out.print("-d must be followed by a location\n", .{}); try out.print("-d must be followed by a location\n", .{});
return errors.InvalidArguments; return errors.InvalidArguments;
} }
extLoc = nxt.?; ext_loc = nxt.?;
continue; continue;
} else if (std.mem.eql(u8, arg, "-p")) { } else if (std.mem.eql(u8, arg, "-p")) {
const nxt = arg_iter.next(); const nxt = arg_iter.next();
+3
View File
@@ -33,6 +33,9 @@ pub fn addFragment(self: *Extractor, data: []u8, offset: u32) void {
} }
pub fn asyncExtract(self: Extractor, io: Io, fil: Io.File) Error!void { pub fn asyncExtract(self: Extractor, io: Io, fil: Io.File) Error!void {
if (self.size == 0) return;
// Initializ the file with the correct size.
try fil.writePositionalAll(io, &.{0}, self.size - 1); try fil.writePositionalAll(io, &.{0}, self.size - 1);
var map = try fil.createMemoryMap(io, .{ .len = self.size, .protection = .{ .write = true } }); var map = try fil.createMemoryMap(io, .{ .len = self.size, .protection = .{ .write = true } });
+3
View File
@@ -90,6 +90,9 @@ pub fn finished(self: *DecompCache, io: Io, offset: u64) void {
std.debug.print("Finished using cache, but cache does not exist: {}\n", .{offset}); std.debug.print("Finished using cache, but cache does not exist: {}\n", .{offset});
return; return;
} }
self.mut.lockUncancelable(io);
defer self.mut.unlock(io);
const use = cache.?.usage.fetchSub(1, .acquire); const use = cache.?.usage.fetchSub(1, .acquire);
if (use == 0) if (use == 0)
self.cond.broadcast(io); self.cond.broadcast(io);
+2
View File
@@ -43,6 +43,8 @@ pub fn init(alloc: std.mem.Allocator, rdr: *Reader, size: u32) Error!Directory {
.type = raw.type, .type = raw.type,
.name = name, .name = name,
}); });
read += @sizeOf(RawEntry) + raw.name_size + 1;
} }
} }
+50 -22
View File
@@ -1,38 +1,43 @@
const std = @import("std"); const std = @import("std");
const Io = std.Io; const Io = std.Io;
const Atomic = std.atomic.Value; const Atomic = std.atomic.Value;
const DecompCache = @import("decomp_cache.zig");
const ExtractionOptions = @import("options.zig");
const Inode = @import("inode.zig");
const Superblock = @import("archive.zig").Superblock;
const Directory = @import("directory.zig");
const DataExtractor = @import("data/extractor.zig"); const DataExtractor = @import("data/extractor.zig");
const DataReader = @import("data/reader.zig"); const DataReader = @import("data/reader.zig");
const DecompCache = @import("decomp_cache.zig");
const Directory = @import("directory.zig");
const ExtractionOptions = @import("options.zig");
const Inode = @import("inode.zig");
const Lookup = @import("lookup.zig"); const Lookup = @import("lookup.zig");
const Superblock = @import("archive.zig").Superblock;
const XattrTable = @import("xattr.zig"); 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 { 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, "/"); const path = std.mem.trim(u8, ext_loc, "/");
var buf: [50]ReturnUnion = undefined; var buf: [1000]ReturnUnion = undefined;
var sel: Io.Select(ReturnUnion) = .init(io, &buf); var sel: Io.Select(ReturnUnion) = .init(io, &buf);
defer { defer {
std.debug.print("starting cancel loop...\n", .{});
while (sel.cancel()) |ret| { while (sel.cancel()) |ret| {
std.debug.print("HELLO!\n", .{});
switch (ret) { switch (ret) {
.dir_ret => |d| { .dir_ret => |d| {
const res = d catch continue; const res = d catch continue;
alloc.free(res.path); if (!res.origin)
alloc.free(res.path);
alloc.destroy(res.sub_files);
}, },
.file_ret => |f| { .file_ret => |f| {
const res = f catch continue; const res = f catch continue;
alloc.free(res.path); if (!res.origin)
alloc.free(res.path);
}, },
else => {}, else => {},
} }
} }
std.debug.print("goodbye\n", .{});
} }
var id_table: Lookup.Table(u16) = .init(alloc, cache, super.id_start, super.id_count); var id_table: Lookup.Table(u16) = .init(alloc, cache, super.id_start, super.id_count);
@@ -46,7 +51,7 @@ pub fn extract(alloc: std.mem.Allocator, io: Io, inode: Inode, cache: *DecompCac
var frag_table: Lookup.Table(Lookup.FragmentEntry) = .init(alloc, cache, super.frag_start, super.frag_count); var frag_table: Lookup.Table(Lookup.FragmentEntry) = .init(alloc, cache, super.frag_start, super.frag_count);
defer frag_table.deinit(); defer frag_table.deinit();
try extractReal(alloc, io, cache, super, &sel, &frag_table, path, inode, null, false); try extractReal(alloc, io, cache, super, &sel, &frag_table, path, inode, null, true);
try ret_loop.await(io); try ret_loop.await(io);
} }
@@ -65,10 +70,6 @@ fn extractReal(
) Error!void { ) Error!void {
io.checkCancel() catch |err| { io.checkCancel() catch |err| {
if (parent != null) _ = parent.?.fetchSub(1, .acquire); if (parent != null) _ = parent.?.fetchSub(1, .acquire);
if (!origin) {
alloc.free(path);
inode.deinit(alloc);
}
return err; return err;
}; };
@@ -108,6 +109,7 @@ fn extractDir(
parent: ?*Atomic(usize), parent: ?*Atomic(usize),
origin: bool, origin: bool,
) Error!DirReturn { ) Error!DirReturn {
std.debug.print("started extract of {s}\n", .{path});
defer { defer {
if (parent != null) if (parent != null)
_ = parent.?.fetchSub(1, .acquire); _ = parent.?.fetchSub(1, .acquire);
@@ -115,6 +117,8 @@ fn extractDir(
} }
errdefer if (!origin) alloc.free(path); errdefer if (!origin) alloc.free(path);
try Io.Dir.cwd().createDirPath(io, path);
const dir = inode.directory(alloc, io, cache, super.dir_start) catch |err| switch (err) { const dir = inode.directory(alloc, io, cache, super.dir_start) catch |err| switch (err) {
error.NotDirectory => unreachable, error.NotDirectory => unreachable,
else => |e| return e, else => |e| return e,
@@ -145,6 +149,7 @@ fn extractDir(
errdefer new_inode.deinit(alloc); errdefer new_inode.deinit(alloc);
const new_path = try std.mem.concat(alloc, u8, &.{ path, "/", entry.name }); const new_path = try std.mem.concat(alloc, u8, &.{ path, "/", entry.name });
errdefer alloc.free(new_path);
try extractReal( try extractReal(
alloc, alloc,
@@ -172,6 +177,7 @@ fn extractFile(
parent: ?*Atomic(usize), parent: ?*Atomic(usize),
origin: bool, origin: bool,
) Error!FileReturn { ) Error!FileReturn {
std.debug.print("started extract of {s}\n", .{path});
defer { defer {
if (parent != null) if (parent != null)
_ = parent.?.fetchSub(1, .acquire); _ = parent.?.fetchSub(1, .acquire);
@@ -230,6 +236,7 @@ fn extractFile(
return ret; return ret;
} }
fn extractSymlink(alloc: std.mem.Allocator, io: Io, path: []const u8, inode: Inode, parent: ?*Atomic(usize), origin: bool) Error!void { 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 { defer {
if (parent != null) if (parent != null)
_ = parent.?.fetchSub(1, .acquire); _ = parent.?.fetchSub(1, .acquire);
@@ -248,6 +255,7 @@ fn extractSymlink(alloc: std.mem.Allocator, io: Io, path: []const u8, inode: Ino
try Io.Dir.cwd().symLink(io, target, path, .{}); 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 { 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 { defer {
if (parent != null) if (parent != null)
_ = parent.?.fetchSub(1, .acquire); _ = parent.?.fetchSub(1, .acquire);
@@ -319,7 +327,8 @@ fn extractNod(alloc: std.mem.Allocator, path: []const u8, inode: Inode, parent:
// Loop // Loop
fn returnLoop(alloc: std.mem.Allocator, io: Io, id_table: *Lookup.Table(u16), xattr_table: *XattrTable, sel: *Io.Select(ReturnUnion), options: ExtractionOptions) !void { fn returnLoop(alloc: std.mem.Allocator, io: Io, id_table: *Lookup.Table(u16), xattr_table: *XattrTable, sel: *Io.Select(ReturnUnion), options: ExtractionOptions) !void {
while (true) { var origin_finished = false;
while (!origin_finished) {
const finished = try sel.await(); const finished = try sel.await();
switch (finished) { switch (finished) {
@@ -328,11 +337,19 @@ fn returnLoop(alloc: std.mem.Allocator, io: Io, id_table: *Lookup.Table(u16), xa
if (ret.sub_files.load(.unordered) != 0) { if (ret.sub_files.load(.unordered) != 0) {
sel.queue.putOne(io, .{ .dir_ret = ret }) catch |err| { sel.queue.putOne(io, .{ .dir_ret = ret }) catch |err| {
if (!ret.origin) alloc.free(ret.path); if (!ret.origin) alloc.free(ret.path);
alloc.destroy(ret.sub_files);
return err; return err;
}; };
continue; continue;
} }
if (!ret.origin) alloc.free(ret.path); 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); alloc.destroy(ret.sub_files);
if (!options.ignore_permissions and (!options.ignore_xattr and ret.xattr_idx != null)) { if (!options.ignore_permissions and (!options.ignore_xattr and ret.xattr_idx != null)) {
@@ -347,7 +364,10 @@ fn returnLoop(alloc: std.mem.Allocator, io: Io, id_table: *Lookup.Table(u16), xa
try file.setOwner(io, try id_table.get(io, ret.uid_idx), try id_table.get(io, ret.gid_idx)); 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) { if (!options.ignore_xattr and ret.xattr_idx != null) {
const xattrs = try xattr_table.get(alloc, io, ret.xattr_idx.?); const xattrs = xattr_table.get(alloc, io, ret.xattr_idx.?) catch |err| {
std.debug.print("YO {}\n", .{err});
return err;
};
defer xattrs.deinit(alloc); defer xattrs.deinit(alloc);
for (xattrs.xattrs) |x| { for (xattrs.xattrs) |x| {
@@ -360,7 +380,14 @@ fn returnLoop(alloc: std.mem.Allocator, io: Io, id_table: *Lookup.Table(u16), xa
}, },
.file_ret => |f| { .file_ret => |f| {
const ret = try f; const ret = try f;
if (!ret.origin) alloc.free(ret.path); 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)) { if (!options.ignore_permissions and (!options.ignore_xattr and ret.xattr_idx != null)) {
const file = try Io.Dir.cwd().openFile(io, ret.path, .{}); const file = try Io.Dir.cwd().openFile(io, ret.path, .{});
@@ -374,7 +401,10 @@ fn returnLoop(alloc: std.mem.Allocator, io: Io, id_table: *Lookup.Table(u16), xa
try file.setOwner(io, try id_table.get(io, ret.uid_idx), try id_table.get(io, ret.gid_idx)); 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) { if (!options.ignore_xattr and ret.xattr_idx != null) {
const xattrs = try xattr_table.get(alloc, io, ret.xattr_idx.?); 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); defer xattrs.deinit(alloc);
for (xattrs.xattrs) |x| { for (xattrs.xattrs) |x| {
@@ -387,8 +417,6 @@ fn returnLoop(alloc: std.mem.Allocator, io: Io, id_table: *Lookup.Table(u16), xa
}, },
.void_ret => |v| try v, .void_ret => |v| try v,
} }
if (sel.group.token.load(.unordered) == null) break;
} }
} }
@@ -401,7 +429,7 @@ const ReturnUnion = union(enum) {
}; };
const Error = error{ Canceled, MknodError } || Directory.Error || Io.Dir.CreateFileAtomicError || Io.File.Atomic.LinkError || const Error = error{ Canceled, MknodError } || Directory.Error || Io.Dir.CreateFileAtomicError || Io.File.Atomic.LinkError ||
DataExtractor.Error || Io.Dir.SymLinkError; DataExtractor.Error || Io.Dir.SymLinkError || Io.Dir.CreateDirPathError;
const FileReturn = struct { const FileReturn = struct {
path: []const u8, path: []const u8,
+30 -21
View File
@@ -3,6 +3,7 @@ const Io = std.Io;
const Archive = @import("archive.zig"); const Archive = @import("archive.zig");
const Directory = @import("directory.zig"); const Directory = @import("directory.zig");
const Extract = @import("extract.zig");
const ExtractionOptions = @import("options.zig"); const ExtractionOptions = @import("options.zig");
const Inode = @import("inode.zig"); const Inode = @import("inode.zig");
@@ -26,7 +27,7 @@ pub fn init(alloc: std.mem.Allocator, archive: *Archive, inode: Inode, name: []c
} }
pub fn initDirEntry(alloc: std.mem.Allocator, io: Io, archive: *Archive, entry: Directory.Entry) !SfsFile { pub fn initDirEntry(alloc: std.mem.Allocator, io: Io, archive: *Archive, entry: Directory.Entry) !SfsFile {
const new_name = try alloc.alloc(u8, entry.name.len); const new_name = try alloc.alloc(u8, entry.name.len);
defer alloc.free(new_name); errdefer alloc.free(new_name);
@memcpy(new_name, entry.name); @memcpy(new_name, entry.name);
return .{ return .{
@@ -62,37 +63,45 @@ pub fn deinit(self: SfsFile) void {
self.alloc.free(self.name); self.alloc.free(self.name);
} }
pub fn isDir(self: *SfsFile) bool {
return switch (self.inode.hdr.type) {
.dir, .ext_dir => true,
else => false,
};
}
/// Attempts to open the filepath if the SfsFile is a directory. /// Attempts to open the filepath if the SfsFile is a directory.
/// If the given path refers to itself (such as "" or "."), a copied SfsFile is returned. /// If the given path refers to itself (such as "" or "."), a copied SfsFile is returned.
pub fn open(self: *SfsFile, alloc: std.mem.Allocator, io: Io, filepath: []const u8) !SfsFile { pub fn open(self: *SfsFile, alloc: std.mem.Allocator, io: Io, filepath: []const u8) !SfsFile {
const path = std.mem.trim(u8, filepath, "/"); const path = std.mem.trim(u8, filepath, "/");
const first_element: []const u8 = std.mem.sliceTo(path, '/'); const first_element: []const u8 = std.mem.sliceTo(path, '/');
var found_file: SfsFile = blk: {
const dir: Directory = try self.inode.directory(alloc, io, &self.archive.cache, self.archive.super.dir_start);
defer dir.deinit(alloc);
const dir: Directory = try self.inode.directory(alloc, io, &self.archive.cache, self.archive.super.dir_start); var cur_slice = dir.entries;
defer dir.deinit(alloc); var idx: usize = undefined;
while (cur_slice.len > 0) {
var cur_slice = dir.entries; idx = cur_slice.len / 2;
var idx: usize = undefined; switch (std.mem.order(u8, first_element, cur_slice[idx].name)) {
while (cur_slice.len > 0) { .eq => break,
idx = cur_slice.len / 2; .lt => cur_slice = cur_slice[0..idx],
switch (std.mem.order(u8, first_element, cur_slice[idx].name)) { .gt => cur_slice = cur_slice[idx..],
.eq => break, }
.lt => cur_slice = cur_slice[0..idx], } else {
.gt => cur_slice = cur_slice[idx..], return error.NotFound;
} }
} else {
return error.NotFound;
}
if (first_element.len == path.len) return .initDirEntry(alloc, io, self.archive, cur_slice[idx]);
if (cur_slice[idx].type != .dir) return error.NotFound;
var tmp_file: SfsFile = try .initDirEntry(alloc, io, self.archive, cur_slice[idx]);
defer tmp_file.deinit();
return tmp_file.open(alloc, io, path[first_element.len..]); break :blk try .initDirEntry(alloc, io, self.archive, cur_slice[idx]);
};
if (first_element.len == path.len) return found_file;
defer found_file.deinit();
if (!found_file.isDir()) return error.NotFound;
return found_file.open(alloc, io, path[first_element.len..]);
} }
const Extract = @import("extract.zig");
pub fn extract(self: *SfsFile, alloc: std.mem.Allocator, io: Io, ext_dir: []const u8, options: ExtractionOptions) !void { pub fn extract(self: *SfsFile, alloc: std.mem.Allocator, io: Io, ext_dir: []const u8, options: ExtractionOptions) !void {
return Extract.extract(alloc, io, self.inode, &self.archive.cache, self.archive.super, ext_dir, options); return Extract.extract(alloc, io, self.inode, &self.archive.cache, self.archive.super, ext_dir, options);
} }
+3 -1
View File
@@ -49,7 +49,9 @@ pub fn Table(comptime T: anytype) type {
.values = .init(alloc), .values = .init(alloc),
}; };
} }
pub fn deinit(self: *LookupTable) void { pub fn deinit(self: *LookupTable, io: Io) void {
self.mut.lockUncancelable(io);
var iter = self.values.valueIterator(); var iter = self.values.valueIterator();
while (iter.next()) |v| while (iter.next()) |v|
self.alloc.free(v.*); self.alloc.free(v.*);
+6 -3
View File
@@ -66,7 +66,8 @@ fn stream(r: *Reader, w: *Writer, limit: Limit) Reader.StreamError!usize {
if (r.seek >= r.end) { if (r.seek >= r.end) {
const self: *MetadataReader = @fieldParentPtr("interface", r); const self: *MetadataReader = @fieldParentPtr("interface", r);
self.advance() catch |err| { self.advance() catch |err| {
std.debug.print("error advancing metadata reader: {}\n", .{err}); if (err != error.Canceled)
std.debug.print("error advancing metadata reader: {}\n", .{err});
return Reader.Error.ReadFailed; return Reader.Error.ReadFailed;
}; };
} }
@@ -79,7 +80,8 @@ fn discard(r: *Reader, limit: Limit) Reader.Error!usize {
if (r.seek >= r.end) { if (r.seek >= r.end) {
const self: *MetadataReader = @fieldParentPtr("interface", r); const self: *MetadataReader = @fieldParentPtr("interface", r);
self.advance() catch |err| { self.advance() catch |err| {
std.debug.print("error advancing metadata reader: {}\n", .{err}); if (err != error.Canceled)
std.debug.print("error advancing metadata reader: {}\n", .{err});
return Reader.Error.ReadFailed; return Reader.Error.ReadFailed;
}; };
} }
@@ -91,7 +93,8 @@ fn readVec(r: *Reader, vec: [][]u8) Reader.Error!usize {
if (r.seek >= r.end) { if (r.seek >= r.end) {
const self: *MetadataReader = @fieldParentPtr("interface", r); const self: *MetadataReader = @fieldParentPtr("interface", r);
self.advance() catch |err| { self.advance() catch |err| {
std.debug.print("error advancing metadata reader: {}\n", .{err}); if (err != error.Canceled)
std.debug.print("error advancing metadata reader: {}\n", .{err});
return Reader.Error.ReadFailed; return Reader.Error.ReadFailed;
}; };
} }