Re-added tests

Fixed compile errors from tests
This commit is contained in:
Caleb J. Gardner
2026-06-16 22:01:44 -05:00
parent 9ecab7f2a4
commit 42a92853a4
16 changed files with 253 additions and 103 deletions
+1 -1
View File
@@ -67,7 +67,7 @@ pub fn extract(self: Archive, alloc: std.mem.Allocator, io: Io, location: []cons
);
defer root_inode.deinit(alloc);
root_inode.extract(alloc, io, self.super, self.map.memory, self.decomp, location, options);
return root_inode.extract(alloc, io, self.super, self.map.memory, self.decomp, location, options);
}
// Superblock
+10 -4
View File
@@ -7,9 +7,9 @@ const Error = @import("decomp.zig").Error;
pub fn zlib(_: std.mem.Allocator, in: []u8, out: []u8) Error!usize {
var stream: c.z_stream = .{
.next_in = in.ptr,
.avail_in = in.len,
.avail_in = @truncate(in.len),
.next_out = out.ptr,
.avail_out = out.len,
.avail_out = @truncate(out.len),
};
var res = c.inflateInit(&stream);
if (res != c.Z_OK)
@@ -30,7 +30,7 @@ pub fn lzma(_: std.mem.Allocator, in: []u8, out: []u8) Error!usize {
if (res != c.LZMA_OK)
return Error.DecompressionFailed;
while (res == c.LZMA_OK)
res = c.lzma_code(&stream);
res = c.lzma_code(&stream, c.LZMA_RUN);
if (res != c.LZMA_STREAM_END)
return Error.DecompressionFailed;
return stream.total_out;
@@ -40,9 +40,15 @@ pub fn lz4(_: std.mem.Allocator, in: []u8, out: []u8) Error!usize {
in.ptr,
out.ptr,
@intCast(in.len),
out.len,
@intCast(out.len),
);
if (res < 0)
return Error.DecompressionFailed;
return @abs(res);
}
pub fn zstd(_: std.mem.Allocator, in: []u8, out: []u8) Error!usize {
const res = c.ZSTD_decompress(out.ptr, out.len, in.ptr, in.len);
if (c.ZSTD_isError(res) != 0)
return Error.DecompressionFailed;
return res;
}
+6 -6
View File
@@ -19,7 +19,7 @@ size: u64,
frag_data: ?[]u8 = null,
frag_offset: u32 = 0,
cache: ?Cache = null,
cache: ?*Cache = null,
pub fn init(data: []u8, decomp: Decomp.Fn, block_size: u32, blocks: []DataBlock, start: u64, size: u64) Extractor {
return .{
@@ -37,7 +37,7 @@ pub fn addFrag(self: *Extractor, frag_data: []u8, frag_offset: u32) void {
self.frag_data = frag_data;
self.frag_offset = frag_offset;
}
pub fn addCache(self: *Extractor, cache: Cache) void {
pub fn addCache(self: *Extractor, cache: *Cache) void {
self.cache = cache;
}
@@ -57,7 +57,7 @@ pub fn extractAsync(self: Extractor, alloc: std.mem.Allocator, io: Io, file: Io.
var read_offset: u64 = self.start;
for (0.., self.blocks) |i, block| {
group.async(io, blockThread, .{ self, alloc, map.memory, read_offset, i, &err });
group.async(io, blockThread, .{ self, alloc, io, map.memory, read_offset, @truncate(i), &err });
read_offset += block.size;
}
if (self.frag_data != null)
@@ -66,12 +66,12 @@ pub fn extractAsync(self: Extractor, alloc: std.mem.Allocator, io: Io, file: Io.
try group.await(io);
if (err != null)
return err;
return err.?;
try map.write(io);
}
fn blockThread(self: Extractor, alloc: std.mem.Allocator, map_data: []u8, read_offset: u64, block_idx: u32, err: *?Error) error{Canceled}!void {
fn blockThread(self: Extractor, alloc: std.mem.Allocator, io: Io, map_data: []u8, read_offset: u64, block_idx: u32, err: *?Error) error{Canceled}!void {
const size = if (self.frag_data == null and block_idx == (self.size - 1 / self.block_size))
self.size % self.block_size
else
@@ -93,7 +93,7 @@ fn blockThread(self: Extractor, alloc: std.mem.Allocator, map_data: []u8, read_o
}
if (self.cache != null) {
const decomp_block = self.cache.?.get(read_offset, block.size) catch |inner_err| {
const decomp_block = self.cache.?.get(io, read_offset, block.size) catch |inner_err| {
switch (inner_err) {
error.Canceled => return error.Canceled,
else => |e| err.* = e,
+8 -6
View File
@@ -24,7 +24,8 @@ sparse_block: bool = false,
frag_data: ?[]u8 = null,
frag_offset: u32 = 0,
cache: ?Cache = null,
io: ?Io = null,
cache: ?*Cache = null,
block: [1024 * 1024]u8 = undefined,
@@ -58,7 +59,8 @@ pub fn addFrag(self: *Reader, frag_data: []u8, frag_offset: u32) void {
self.frag_data = frag_data;
self.frag_offset = frag_offset;
}
pub fn addCache(self: *Reader, cache: Cache) void {
pub fn addCache(self: *Reader, io: Io, cache: *Cache) void {
self.io = io;
self.cache = cache;
}
@@ -92,7 +94,7 @@ fn advance(self: *Reader) Io.Reader.Error!void {
if (block.size == 0) {
self.sparse_block = true;
self.end = size;
self.interface.end = size;
return;
} else {
self.sparse_block = false;
@@ -109,7 +111,7 @@ fn advance(self: *Reader) Io.Reader.Error!void {
self.interface.buffer = self.block[0..size];
self.interface.end = size;
} else {
self.interface.buffer = self.cache.?.get(self.io, self.offset, block.size) catch return error.ReadFailed;
self.interface.buffer = self.cache.?.get(self.io.?, self.offset, block.size) catch return error.ReadFailed;
self.interface.end = self.interface.buffer.len;
}
}
@@ -119,7 +121,7 @@ fn stream(r: *Io.Reader, w: *Io.Writer, limit: Io.Limit) Io.Reader.StreamError!u
if (r.seek >= r.end)
try self.advance();
if (limit == .nothing) return;
if (limit == .nothing) return 0;
const to_write = @min(@intFromEnum(limit), r.end - r.seek);
@@ -136,7 +138,7 @@ fn discard(r: *Io.Reader, limit: Io.Limit) Io.Reader.Error!usize {
const self: *Reader = @fieldParentPtr("interface", r);
try self.advance();
}
if (limit == .nothing) return;
if (limit == .nothing) return 0;
const to_discard = @min(@intFromEnum(limit), r.end - r.seek);
+3 -3
View File
@@ -6,10 +6,10 @@ const zig = @import("zig_decomp.zig");
pub fn getFn(e: Enum) !Fn {
return switch (e) {
.gzip => if (build.use_zig_decomp) zig.gzip else c.gzip,
.gzip => if (build.use_zig_decomp) zig.zlib else c.zlib,
.lzma => if (build.use_zig_decomp) zig.lzma else c.lzma,
.lzo => if (build.use_zig_decomp or !build.allow_lzo) error.LzoUnsupported else c.lzo,
.xz => if (build.use_zig_decomp) zig.xz else c.xz,
.xz => if (build.use_zig_decomp) zig.xz else c.lzma,
.lz4 => if (build.use_zig_decomp) error.Lz4Unsupported else c.lz4,
.zstd => if (build.use_zig_decomp) zig.zstd else c.zstd,
};
@@ -17,7 +17,7 @@ pub fn getFn(e: Enum) !Fn {
// Types
pub const Fn = *fn (std.mem.Allocator, in: []u8, out: []u8) Error!usize;
pub const Fn = *const fn (std.mem.Allocator, in: []u8, out: []u8) Error!usize;
pub const Enum = enum(u16) {
gzip = 1,
+5 -5
View File
@@ -15,7 +15,7 @@ pub fn init(alloc: std.mem.Allocator, rdr: *Reader, size: u64) !Directory {
var read: u64 = 3;
var out: std.ArrayList(Entry) = .initCapacity(alloc, 50);
var out: std.ArrayList(Entry) = try .initCapacity(alloc, 50);
errdefer {
for (out.items) |entry|
entry.deinit(alloc);
@@ -29,13 +29,13 @@ pub fn init(alloc: std.mem.Allocator, rdr: *Reader, size: u64) !Directory {
try out.ensureUnusedCapacity(alloc, hdr.count + 1);
for (0..hdr.count + 1) |_| {
try rdr.readSliceEndian(Entry, @ptrCast(&raw), .little);
try rdr.readSliceEndian(RawEntry, @ptrCast(&raw), .little);
const name = try alloc.alloc(u8, raw.name_size + 1);
try rdr.readSliceEndian(u8, name, .little);
const entry = out.addOneAssumeCapacity(alloc);
const entry = out.addOneAssumeCapacity();
entry.* = .{
.name = name,
@@ -48,7 +48,7 @@ pub fn init(alloc: std.mem.Allocator, rdr: *Reader, size: u64) !Directory {
}
}
return out.toOwnedSlice(alloc);
return .{ .entries = try out.toOwnedSlice(alloc) };
}
pub fn deinit(self: Directory, alloc: std.mem.Allocator) void {
for (self.entries) |entry|
@@ -64,7 +64,7 @@ pub const Entry = struct {
name: []const u8,
block_start: u32,
block_offset: u16,
type: Inode.type,
type: Inode.Type,
pub fn deinit(self: Entry, alloc: std.mem.Allocator) void {
alloc.free(self.name);
+66 -27
View File
@@ -22,7 +22,7 @@ pub fn extract(
ext_loc: []const u8,
options: ExtractionOptions,
) !void {
const path = std.mem.trim(ext_loc, "/");
const path = std.mem.trim(u8, ext_loc, "/");
var id_table: Lookup.Table(u16) = .init(alloc, data, decomp, super.id_start, super.id_count);
defer id_table.deinit();
@@ -42,7 +42,7 @@ pub fn extract(
else => {},
};
var loop = io.async(io, finishLoop, .{ alloc, io, &sel, &id_table, &xattr_table, inode.hdr.num, options });
var loop = io.async(finishLoop, .{ alloc, io, &sel, &id_table, &xattr_table, inode.hdr.num, options });
var cache: Cache = .init(alloc, data, decomp);
defer cache.deinit();
@@ -51,22 +51,22 @@ pub fn extract(
defer frag_table.deinit();
switch (inode.hdr.type) {
.file, .ext_file => try sel.async(
.file, .ext_file => sel.async(
.path,
extractFile,
.{ alloc, io, data, decomp, super.block_size, &cache, &frag_table, inode, path, true },
),
.dir, .ext_dir => try sel.async(
.dir, .ext_dir => sel.async(
.path,
extractDir,
.{ alloc, io, super, data, decomp, &sel, &cache, &frag_table, inode, path, true },
),
.symlink, .ext_symlink => try sel.async(
.symlink, .ext_symlink => sel.async(
.void,
extractSymlink,
.{ alloc, io, inode, path, true },
),
else => try sel.async(
else => sel.async(
.path,
extractNod,
.{ alloc, inode, path, true },
@@ -76,7 +76,7 @@ pub fn extract(
try loop.await(io);
}
fn dirOrder(a: PathReturn, b: PathReturn) std.math.Order {
fn dirOrder(_: void, a: PathReturn, b: PathReturn) std.math.Order {
return std.math.order(std.mem.count(u8, a.path, "/"), std.mem.count(u8, b.path, "/"));
}
fn finishLoop(alloc: std.mem.Allocator, io: Io, sel: *Io.Select(ReturnUnion), id_table: *Lookup.Table(u16), xattr_table: *XattrTable, start_num: u32, options: ExtractionOptions) !void {
@@ -88,12 +88,19 @@ fn finishLoop(alloc: std.mem.Allocator, io: Io, sel: *Io.Select(ReturnUnion), id
while (true) {
const value: ReturnUnion = try sel.await();
switch (value) {
.void => continue,
else => {},
}
const path_ret = switch (value) {
.void => {
_ = sel.group.token.load(.unordered) orelse break;
continue;
},
.path => |p| try p,
};
const path_ret: PathReturn = value.path;
if (options.ignore_permissions and (options.ignore_xattr or path_ret.xattr_idx == null)) {
if (path_ret.hdr.num != start_num)
alloc.free(path_ret.path);
continue;
}
if (path_ret.hdr.type == .dir or path_ret.hdr.type == .ext_dir) {
dirs.push(alloc, path_ret) catch |err| {
@@ -106,17 +113,43 @@ fn finishLoop(alloc: std.mem.Allocator, io: Io, sel: *Io.Select(ReturnUnion), id
defer if (path_ret.hdr.num != start_num)
alloc.free(path_ret.path);
if (options.ignore_permissions and (options.ignore_xattr or path_ret.xattr_idx == null)) continue;
var file = try Io.Dir.cwd().openFile(io, path_ret.path, .{});
defer file.close();
defer file.close(io);
if (!options.ignore_xattr and path_ret.xattr_idx != null) {
const xattr = try xattr_table.get(alloc, io, path_ret.xattr_idx.?);
defer xattr.deinit(alloc);
for (xattr.kvs) |kv| {
const res = std.os.linux.fsetxattr(file.handle, kv.key, kv.value, kv.value.len, 0);
const res = std.os.linux.fsetxattr(file.handle, kv.key, kv.value.ptr, kv.value.len, 0);
if (res != 0)
return error.SetXattrError;
}
}
if (!options.ignore_permissions) {
try file.setTimestamps(io, .{
.modify_timestamp = .init(Io.Timestamp.fromNanoseconds(@as(i96, @intCast(path_ret.hdr.mod_time)) * std.time.ns_per_s)),
});
try file.setPermissions(io, @enumFromInt(path_ret.hdr.permissions));
try file.setOwner(io, try id_table.get(io, path_ret.hdr.uid_idx), try id_table.get(io, path_ret.hdr.gid_idx));
}
_ = sel.group.token.load(.unordered) orelse break;
}
while (dirs.popMax()) |path_ret| {
if (path_ret.hdr.num != start_num)
alloc.free(path_ret.path);
var file = try Io.Dir.cwd().openFile(io, path_ret.path, .{});
defer file.close(io);
if (!options.ignore_xattr and path_ret.xattr_idx != null) {
const xattr = try xattr_table.get(alloc, io, path_ret.xattr_idx.?);
defer xattr.deinit(alloc);
for (xattr.kvs) |kv| {
const res = std.os.linux.fsetxattr(file.handle, kv.key, kv.value.ptr, kv.value.len, 0);
if (res != 0)
return error.SetXattrError;
}
@@ -159,7 +192,7 @@ fn extractDir(
var meta: MetadataReader = .init(alloc, data, decomp, d.block_start);
try meta.interface.discardAll(d.block_offset);
break :blk Directory.init(alloc, &meta.interface, d.size);
break :blk try Directory.init(alloc, &meta.interface, d.size);
},
.ext_dir => |d| blk: {
if (d.xattr_idx != 0xFFFFFFFF) ret.xattr_idx = d.xattr_idx;
@@ -167,7 +200,7 @@ fn extractDir(
var meta: MetadataReader = .init(alloc, data, decomp, d.block_start);
try meta.interface.discardAll(d.block_offset);
break :blk Directory.init(alloc, &meta.interface, d.size);
break :blk try Directory.init(alloc, &meta.interface, d.size);
},
else => unreachable,
};
@@ -176,7 +209,7 @@ fn extractDir(
for (dir.entries) |entry| {
var new_inode: Inode = try .initEntry(alloc, data, decomp, super.inode_start, super.block_size, entry);
const new_path = std.mem.concat(alloc, &.{ path, "/", entry.name }) catch |err| {
const new_path = std.mem.concat(alloc, u8, &.{ path, "/", entry.name }) catch |err| {
new_inode.deinit(alloc);
return err;
};
@@ -184,10 +217,12 @@ fn extractDir(
switch (entry.type) {
.dir => sel.async(.path, extractDir, .{ alloc, io, super, data, decomp, sel, cache, frag_table, new_inode, new_path, false }),
.file => sel.async(.path, extractFile, .{ alloc, io, data, decomp, super.block_size, cache, frag_table, new_inode, new_path, false }),
.symlink => sel.async(.void, extractSymlink, .{ alloc, io, data, decomp, new_inode, new_path, false }),
else => sel.async(.path, extractNod, .{ alloc, data, decomp, new_inode, new_path, false }),
.symlink => sel.async(.void, extractSymlink, .{ alloc, io, new_inode, new_path, false }),
else => sel.async(.path, extractNod, .{ alloc, new_inode, new_path, false }),
}
}
return ret;
}
fn extractFile(
alloc: std.mem.Allocator,
@@ -220,10 +255,12 @@ fn extractFile(
const entry: Lookup.FragEntry = try frag_table.get(io, f.frag_idx);
if (entry.size.uncompressed) {
rdr.addFrag(data[entry.block_start..][0..entry.size.size]);
rdr.addFrag(data[entry.block_start..][0..entry.size.size], f.frag_offset);
} else {
rdr.addFrag(try cache.get(io, entry.block_start, entry.size));
rdr.addFrag(try cache.get(io, entry.block_start, entry.size.size), f.frag_offset);
}
break :blk rdr;
},
.ext_file => |f| blk: {
if (f.xattr_idx != 0xFFFFFFFF) ret.xattr_idx = f.xattr_idx;
@@ -235,10 +272,12 @@ fn extractFile(
const entry: Lookup.FragEntry = try frag_table.get(io, f.frag_idx);
if (entry.size.uncompressed) {
rdr.addFrag(data[entry.block_start..][0..entry.size.size]);
rdr.addFrag(data[entry.block_start..][0..entry.size.size], f.frag_offset);
} else {
rdr.addFrag(try cache.get(io, entry.block_start, entry.size));
rdr.addFrag(try cache.get(io, entry.block_start, entry.size.size), f.frag_offset);
}
break :blk rdr;
},
else => unreachable,
};
@@ -329,11 +368,11 @@ fn extractNod(alloc: std.mem.Allocator, inode: Inode, path: []const u8, origin:
const ReturnUnion = union(enum) {
path: Error!PathReturn,
dir: Error!PathReturn,
void: Error!void,
};
const Error = error{} || Decomp.Error || Directory.Error || DataExtractor.Error;
const Error = error{MknodError} || Decomp.Error || Directory.Error || DataExtractor.Error || Io.Dir.CreateDirPathError ||
Io.Dir.SymLinkError || Io.File.Atomic.LinkError;
const PathReturn = struct {
hdr: Inode.Header,
+15 -11
View File
@@ -22,7 +22,7 @@ pub fn extract(
ext_loc: []const u8,
options: ExtractionOptions,
) !void {
const path = std.mem.trim(ext_loc, "/");
const path = std.mem.trim(u8, ext_loc, "/");
var cache: Cache = .init(alloc, data, decomp);
defer cache.deinit();
@@ -84,7 +84,7 @@ pub fn extractReal(
var meta: MetadaReader = .init(alloc, data, decomp, d.block_start);
try meta.interface.discardAll(d.block_offset);
break :blk Directory.init(alloc, &meta.interface, d.size);
break :blk try Directory.init(alloc, &meta.interface, d.size);
},
.ext_dir => |d| blk: {
if (d.xattr_idx != 0xFFFFFFFF) xattr_idx = d.xattr_idx;
@@ -92,7 +92,7 @@ pub fn extractReal(
var meta: MetadaReader = .init(alloc, data, decomp, d.block_start);
try meta.interface.discardAll(d.block_offset);
break :blk Directory.init(alloc, &meta.interface, d.size);
break :blk try Directory.init(alloc, &meta.interface, d.size);
},
else => unreachable,
};
@@ -101,7 +101,7 @@ pub fn extractReal(
for (dir.entries) |entry| {
var new_inode: Inode = try .initEntry(alloc, data, decomp, super.inode_start, super.block_size, entry);
const new_path = std.mem.concat(alloc, &.{ path, "/", entry.name }) catch |err| {
const new_path = std.mem.concat(alloc, u8, &.{ path, "/", entry.name }) catch |err| {
new_inode.deinit(alloc);
return err;
};
@@ -113,31 +113,35 @@ pub fn extractReal(
var rdr: DataReader = switch (inode.data) {
.file => |f| blk: {
var rdr: DataReader = .init(alloc, data, decomp, super.block_size, f.blocks, f.size, f.block_start);
rdr.addCache(cache);
rdr.addCache(io, cache);
if (f.frag_idx == 0xFFFFFFFF) break :blk rdr;
const entry: Lookup.FragEntry = try frag_table.get(io, f.frag_idx);
if (entry.size.uncompressed) {
rdr.addFrag(data[entry.block_start..][0..entry.size.size]);
rdr.addFrag(data[entry.block_start..][0..entry.size.size], f.frag_offset);
} else {
rdr.addFrag(try cache.get(io, entry.block_start, entry.size));
rdr.addFrag(try cache.get(io, entry.block_start, entry.size.size), f.frag_offset);
}
break :blk rdr;
},
.ext_file => |f| blk: {
if (f.xattr_idx != 0xFFFFFFFF) xattr_idx = f.xattr_idx;
var rdr: DataReader = .init(alloc, data, decomp, super.block_size, f.blocks, f.size, f.block_start);
rdr.addCache(cache);
rdr.addCache(io, cache);
if (f.frag_idx == 0xFFFFFFFF) break :blk rdr;
const entry: Lookup.FragEntry = try frag_table.get(io, f.frag_idx);
if (entry.size.uncompressed) {
rdr.addFrag(data[entry.block_start..][0..entry.size.size]);
rdr.addFrag(data[entry.block_start..][0..entry.size.size], f.frag_offset);
} else {
rdr.addFrag(try cache.get(io, entry.block_start, entry.size));
rdr.addFrag(try cache.get(io, entry.block_start, entry.size.size), f.frag_offset);
}
break :blk rdr;
},
else => unreachable,
};
@@ -221,7 +225,7 @@ pub fn extractReal(
defer xattr.deinit(alloc);
for (xattr.kvs) |kv| {
const res = std.os.linux.fsetxattr(fil.handle, kv.key, kv.value, kv.value.len, 0);
const res = std.os.linux.fsetxattr(fil.handle, kv.key, kv.value.ptr, kv.value.len, 0);
if (res != 0)
return error.SetXattrError;
}
+10 -10
View File
@@ -40,7 +40,7 @@ pub fn initEntry(alloc: std.mem.Allocator, super: Superblock, data: []u8, decomp
const inode: Inode = try .initEntry(alloc, data, decomp, super.inode_start, super.block_size, entry);
errdefer inode.deinit(alloc);
const new_name = try alloc.dupe(entry.name);
const new_name = try alloc.dupe(u8, entry.name);
return init(alloc, super, data, decomp, new_name, inode);
}
@@ -49,22 +49,22 @@ pub fn initRef(alloc: std.mem.Allocator, super: Superblock, data: []u8, decomp:
const inode: Inode = try .initRef(alloc, data, decomp, super.inode_start, super.block_size, ref);
errdefer inode.deinit(alloc);
const new_name = try alloc.dupe(name);
const new_name = try alloc.dupe(u8, name);
return init(alloc, super, data, decomp, new_name, inode);
}
pub fn copy(self: File, alloc: std.mem.Allocator) !File {
const inode = self.inode;
var inode = self.inode;
switch (inode.data) {
.file => |*f| f.blocks = try alloc.dupe(self.inode.data.file.blocks),
.ext_file => |*f| f.blocks = try alloc.dupe(self.inode.data.file.blocks),
.symlink => |*s| s.target = try alloc.dupe(self.inode.data.symlink.target),
.ext_symlink => |*s| s.target = try alloc.dupe(self.inode.data.symlink.target),
.file => |*f| f.blocks = try alloc.dupe(Inode.DataBlock, self.inode.data.file.blocks),
.ext_file => |*f| f.blocks = try alloc.dupe(Inode.DataBlock, self.inode.data.file.blocks),
.symlink => |*s| s.target = try alloc.dupe(u8, self.inode.data.symlink.target),
.ext_symlink => |*s| s.target = try alloc.dupe(u8, self.inode.data.symlink.target),
else => {},
}
errdefer inode.deinit(alloc);
const new_name = try alloc.dupe(self.name);
const new_name = try alloc.dupe(u8, self.name);
return init(alloc, self.super, self.data, self.decomp, new_name, inode);
}
@@ -92,7 +92,7 @@ pub fn open(self: File, alloc: std.mem.Allocator, filepath: []const u8) !File {
try meta.interface.discardAll(d.block_offset);
break :blk meta;
},
else => error.NotDirectory,
else => return error.NotDirectory,
};
const path = std.mem.trim(u8, filepath, "/");
@@ -100,7 +100,7 @@ pub fn open(self: File, alloc: std.mem.Allocator, filepath: []const u8) !File {
if (path.len == 0 or (path.len == 1 and path[0] == '.'))
return self.copy(alloc);
const first_element: []u8 = std.mem.sliceTo(path, '/');
const first_element: []const u8 = std.mem.sliceTo(path, '/');
const file: File = blk: {
var directory: Directory = try .init(alloc, &meta.interface, size);
+5 -5
View File
@@ -40,13 +40,13 @@ pub fn initRef(alloc: std.mem.Allocator, data: []u8, decomp: Decomp.Fn, inode_st
var meta: MetadataReader = .init(alloc, data, decomp, inode_start + ref.block_start);
try meta.interface.discardAll(ref.block_offset);
return .init(alloc, &meta.interface, block_size);
return .init(alloc, block_size, &meta.interface);
}
pub fn initEntry(alloc: std.mem.Allocator, data: []u8, decomp: Decomp.Fn, inode_start: u64, block_size: u32, entry: Directory.Entry) !Inode {
var meta: MetadataReader = .init(alloc, data, decomp, inode_start + entry.block_start);
try meta.interface.discardAll(entry.block_offset);
return .init(alloc, &meta.interface, block_size);
return .init(alloc, block_size, &meta.interface);
}
pub fn deinit(self: Inode, alloc: std.mem.Allocator) void {
switch (self.data) {
@@ -64,7 +64,7 @@ pub fn extract(self: Inode, alloc: std.mem.Allocator, io: Io, super: Superblock,
// Types
pub const Ref = packed struct {
pub const Ref = packed struct(u64) {
block_offset: u16,
block_start: u32,
_: u16,
@@ -191,7 +191,7 @@ pub const ExtFile = struct {
var data: [40]u8 = undefined;
try rdr.readSliceAll(&data);
const frag_idx = std.mem.readInt(u32, data[28..], .little);
const frag_idx = std.mem.readInt(u32, data[28..32], .little);
const size = std.mem.readInt(u64, data[8..16], .little);
var blocks_num = size / block_size;
@@ -238,7 +238,7 @@ pub const ExtSymlink = struct {
xattr_idx: u32,
fn init(alloc: std.mem.Allocator, rdr: *Reader) !ExtSymlink {
const sym: Symlink = .init(alloc, rdr);
const sym: Symlink = try .init(alloc, rdr);
var xattr_idx: u32 = undefined;
try rdr.readSliceEndian(u32, @ptrCast(&xattr_idx), .little);
+24 -9
View File
@@ -4,7 +4,7 @@ const Io = std.Io;
const DataBlock = @import("inode.zig").DataBlock;
const Decomp = @import("decomp.zig");
const MetadataReader = @import("meta_rdr.zig");
const ProtectedMap = @import("util/protected_map.zig");
const ProtectedMap = @import("util/protected_map.zig").ProtectedMap;
pub fn Table(comptime T: anytype) type {
return struct {
@@ -21,7 +21,15 @@ pub fn Table(comptime T: anytype) type {
table: ProtectedMap(u32, []T, getBlock),
pub fn init(alloc: std.mem.Allocator, data: []u8, decomp: Decomp.Fn, table_start: u64, table_num: u32) Self {
return .{ .data = data, .decomp = decomp, .table_start = table_start, .table_num = table_num, .table = .init(alloc) };
return .{
.data = data,
.decomp = decomp,
.table_start = table_start,
.table_num = table_num,
.table = .init(alloc),
};
}
pub fn deinit(self: *Self) void {
var iter = self.table.map.valueIterator();
@@ -38,20 +46,27 @@ pub fn Table(comptime T: anytype) type {
const block = idx / VALUES_PER_BLOCK;
const block_idx = idx % VALUES_PER_BLOCK;
const values = try self.table.getOrPut(io, block, .{ self.*, block });
const values = try self.table.getOrPut(io, block, .{
self.table.alloc,
self.data,
self.decomp,
self.table_start,
self.table_num,
block,
});
return values[block_idx];
return values.*[block_idx];
}
pub fn getBlock(self: Self, block_idx: u32) ![]T {
const offset: u64 = std.mem.readInt(u64, self.data[self.table_start + (block_idx * 8) ..][0..8], .little);
pub fn getBlock(alloc: std.mem.Allocator, data: []u8, decomp: Decomp.Fn, table_start: u64, table_num: u32, block_idx: u32) ![]T {
const offset: u64 = std.mem.readInt(u64, data[table_start + (block_idx * 8) ..][0..8], .little);
const block = try self.table.alloc(T, if (block_idx == (self.table_num - 1 / VALUES_PER_BLOCK))
self.table_num % VALUES_PER_BLOCK
const block = try alloc.alloc(T, if (block_idx == (table_num - 1 / VALUES_PER_BLOCK))
table_num % VALUES_PER_BLOCK
else
VALUES_PER_BLOCK);
var meta: MetadataReader = .init(self.table.alloc, self.data, self.decomp, offset);
var meta: MetadataReader = .init(alloc, data, decomp, offset);
try meta.interface.readSliceEndian(T, block, .little);
return block;
+1 -1
View File
@@ -43,7 +43,7 @@ fn advance(self: *MetadataReader) Reader.Error!void {
self.interface.seek = 0;
errdefer self.interface.end = 0;
const hdr: Header = @intCast(std.mem.readInt(u16, self.data[self.cur_offset..][0..2], .little));
const hdr: Header = @bitCast(std.mem.readInt(u16, self.data[self.cur_offset..][0..2], .little));
defer self.cur_offset += hdr.size;
const block = self.data[self.cur_offset + 2 ..][0..hdr.size];
+81 -4
View File
@@ -6,16 +6,93 @@ const Archive = @import("archive.zig");
const TestArchive = "testing/LinuxPATest.sfs";
test "Basics" {}
test "Basics" {
const io = testing.io;
const alloc = testing.allocator;
var archive_file = try Io.Dir.cwd().openFile(io, TestArchive, .{});
defer archive_file.close(io);
var archive: Archive = try .init(io, archive_file, 0);
defer archive.deinit(io);
try testing.expectEqualDeep(archive.super, LinuxPATestCorrectSuperblock);
var root = try archive.root(alloc);
defer root.deinit();
}
const TestFile = "Start.exe";
const TestFileExtractLocation = "testing/Start.exe";
test "ExtractSingleFile" {}
test "ExtractSingleFile" {
const io = testing.io;
const alloc = testing.allocator;
const TestFullExtractLocation = "testing/TestExtract";
Io.Dir.cwd().deleteFile(io, TestFileExtractLocation) catch {};
test "ExtractCompleteArchive" {}
var archive_file = try Io.Dir.cwd().openFile(io, TestArchive, .{});
defer archive_file.close(io);
var archive: Archive = try .init(io, archive_file, 0);
defer archive.deinit(io);
var start_exe = try archive.open(alloc, TestFile);
defer start_exe.deinit();
try start_exe.extract(alloc, io, TestFileExtractLocation, .default);
}
const TestFullExtractLocationMT = "testing/TestExtractMT";
test "ExtractCompleteArchiveMultiThreaded" {
const io = testing.io;
const alloc = testing.allocator;
Io.Dir.cwd().deleteFile(io, TestFullExtractLocationMT) catch {};
var archive_file = try Io.Dir.cwd().openFile(io, TestArchive, .{});
defer archive_file.close(io);
var archive: Archive = try .init(io, archive_file, 0);
defer archive.deinit(io);
try archive.extract(alloc, io, TestFullExtractLocationMT, .default);
}
const TestFullExtractLocationSTOption = "testing/TestExtractSTOption";
test "ExtractCompleteArchiveSingleThreadedOption" {
const io = testing.io;
const alloc = testing.allocator;
Io.Dir.cwd().deleteFile(io, TestFullExtractLocationSTOption) catch {};
var archive_file = try Io.Dir.cwd().openFile(io, TestArchive, .{});
defer archive_file.close(io);
var archive: Archive = try .init(io, archive_file, 0);
defer archive.deinit(io);
try archive.extract(alloc, io, TestFullExtractLocationSTOption, .single_threaded_default);
}
const TestFullExtractLocationSTIo = "testing/TestExtractSTIo";
test "ExtractCompleteArchiveSingleThreadedIo" {
const io = Io.Threaded.global_single_threaded.io();
const alloc = testing.allocator;
Io.Dir.cwd().deleteFile(io, TestFullExtractLocationSTIo) catch {};
var archive_file = try Io.Dir.cwd().openFile(io, TestArchive, .{});
defer archive_file.close(io);
var archive: Archive = try .init(io, archive_file, 0);
defer archive.deinit(io);
try archive.extract(alloc, io, TestFullExtractLocationSTIo, .default);
}
const LinuxPATestCorrectSuperblock: Archive.Superblock = .{
.magic = std.mem.readInt(u32, "hsqs", .little),
+1 -1
View File
@@ -29,7 +29,7 @@ pub fn deinit(self: *Cache) void {
}
pub fn get(self: *Cache, io: Io, offset: u64, compressed_size: u32) Error![]u8 {
const res: CachedBlock = try self.cache.getOrPut(io, offset, .{ self.alloc, self.data, self.decomp, offset, compressed_size });
const res: *CachedBlock = try self.cache.getOrPut(io, offset, .{ self.alloc, self.data, self.decomp, offset, compressed_size });
return res.block[0..res.size];
}
+14 -9
View File
@@ -2,12 +2,14 @@ const std = @import("std");
const Io = std.Io;
pub fn ProtectedMap(comptime K: anytype, comptime T: anytype, comptime create_fn: anytype) type {
std.debug.assert(std.meta.activeTag(@typeInfo(create_fn)) == .@"fn");
const fn_info = @typeInfo(@TypeOf(create_fn));
std.debug.assert(std.meta.activeTag(fn_info) == .@"fn");
const ret_info = @typeInfo(create_fn).@"fn".return_type;
const ret_info = fn_info.@"fn".return_type;
std.debug.assert(ret_info != null);
std.debug.assert(ret_info == T or
(std.meta.activeTag(@typeInfo(ret_info)) == .error_union and @typeInfo(ret_info).error_union.payload == T));
(std.meta.activeTag(@typeInfo(ret_info.?)) == .error_union and @typeInfo(ret_info.?).error_union.payload == T));
return struct {
const Map = @This();
@@ -28,7 +30,7 @@ pub fn ProtectedMap(comptime K: anytype, comptime T: anytype, comptime create_fn
self.map.deinit();
}
pub fn getOrPut(self: *Map, io: Io, key: K, create_fn_args: std.meta.ArgsTuple(create_fn)) Error!*T {
pub fn getOrPut(self: *Map, io: Io, key: K, create_fn_args: std.meta.ArgsTuple(@TypeOf(create_fn))) Error!*T {
{
try self.mut.lockShared(io);
defer self.mut.unlockShared(io);
@@ -36,9 +38,9 @@ pub fn ProtectedMap(comptime K: anytype, comptime T: anytype, comptime create_fn
const value = self.map.getPtr(key);
if (value != null) {
if (!value.?.filled.isSet())
value.?.filled.wait(io);
if (value.?.err != null) return value.?.err != null;
return value.?.value;
try value.?.filled.wait(io);
if (value.?.err != null) return value.?.err.?;
return &value.?.value;
}
}
try self.mut.lock(io);
@@ -54,7 +56,7 @@ pub fn ProtectedMap(comptime K: anytype, comptime T: anytype, comptime create_fn
res.value_ptr.* = .{};
defer res.value_ptr.filled.set(io);
res.mut.unlock(io);
self.mut.unlock(io);
self.mut.lockSharedUncancelable(io);
defer self.mut.unlockShared(io);
@@ -64,8 +66,11 @@ pub fn ProtectedMap(comptime K: anytype, comptime T: anytype, comptime create_fn
} else {
res.value_ptr.value = @call(.auto, create_fn, create_fn_args) catch |err| {
res.value_ptr.err = err;
return err;
};
}
return &res.value_ptr.value;
}
// Map Types
@@ -73,7 +78,7 @@ pub fn ProtectedMap(comptime K: anytype, comptime T: anytype, comptime create_fn
pub const Error = error{ Canceled, OutOfMemory } ||
if (@TypeOf(CreateError) == void) error{} else CreateError;
const CreateError: type = switch (@typeInfo(@typeInfo(create_fn).@"fn".return_type)) {
const CreateError: type = switch (@typeInfo(ret_info.?)) {
.error_union => |e| e.error_set,
else => void,
};
+3 -1
View File
@@ -28,7 +28,7 @@ pub fn deinit(self: *XattrTable) void {
pub fn get(self: *XattrTable, alloc: std.mem.Allocator, io: Io, idx: u32) !Xattr {
const entry: Entry = try self.table.get(io, idx);
const out: std.ArrayList(KeyValue) = try .initCapacity(alloc, entry.count);
var out: std.ArrayList(KeyValue) = try .initCapacity(alloc, entry.count);
errdefer {
for (out.items) |kv|
kv.deinit(alloc);
@@ -75,6 +75,8 @@ pub fn get(self: *XattrTable, alloc: std.mem.Allocator, io: Io, idx: u32) !Xattr
.value = value,
};
}
return .{ .kvs = try out.toOwnedSlice(alloc) };
}
fn readValue(alloc: std.mem.Allocator, rdr: *Io.Reader) ![]u8 {