Updated a lot of packed structs to extern struct

Specified int types for remaining packed structs
Instead of manually decoding File & ExtFile structs, decode an extern struct first
Fixed some zstd issues
Some more File stuff
This commit is contained in:
Caleb Gardner
2026-05-02 06:10:24 -05:00
parent ab606bdfa5
commit a3f7b86e67
11 changed files with 190 additions and 63 deletions
+1 -1
View File
@@ -11,7 +11,7 @@
"build": {
"command": "zig",
"args": ["build", "-Duse_c_libs=true", "-Ddebug=true"],
"args": ["build", "-Ddebug=true"],
},
"program": "zig-out/bin/unsquashfs",
+34 -7
View File
@@ -48,7 +48,7 @@ pub fn root(self: Archive, alloc: std.mem.Allocator, io: Io) !File {
self.super.block_size,
self.super.root_ref,
);
return .init(alloc, root_inode, "");
return .init(alloc, self, root_inode, "");
}
/// Opens a File within the archive.
pub fn open(self: Archive, alloc: std.mem.Allocator, io: Io, filepath: []const u8) !File {
@@ -61,9 +61,16 @@ pub fn open(self: Archive, alloc: std.mem.Allocator, io: Io, filepath: []const u
}
/// Extract the entire archive contents to the given directory.
pub fn extract(self: Archive, alloc: std.mem.Allocator, io: Io, extract_dir: []const u8, options: ExtractionOptions) !void {
_ = self;
_ = alloc;
_ = io;
const root_inode = try Utils.inodeFromRef(
alloc,
io,
self.file,
&self.stateless_decomp,
self.super.inode_start,
self.super.block_size,
self.super.root_ref,
);
_ = root_inode;
_ = extract_dir;
_ = options;
return error.TODO;
@@ -74,7 +81,15 @@ pub fn extract(self: Archive, alloc: std.mem.Allocator, io: Io, extract_dir: []c
pub fn inode(self: Archive, alloc: std.mem.Allocator, io: Io, num: u32) !Inode {
if (!self.super.flags.exportable)
return error.NotExportable;
const ref = try LookupTable.lookupValue(Inode.Ref, alloc, io, &self.stateless_decomp, self.file, self.super.export_start, num + 1);
const ref = try LookupTable.lookupValue(
Inode.Ref,
alloc,
io,
&self.stateless_decomp,
self.file,
self.super.export_start,
num + 1,
);
return Utils.inodeFromRef(
alloc,
io,
@@ -85,6 +100,18 @@ pub fn inode(self: Archive, alloc: std.mem.Allocator, io: Io, num: u32) !Inode {
ref,
);
}
/// Returns a value at the given index from the Archive's id (uid/gid) table.
pub fn idTable(self: Archive, alloc: std.mem.Allocator, io: Io, idx: u32) !u16 {
return LookupTable.lookupValue(
u16,
alloc,
io,
&self.stateless_decomp,
self.file,
self.super.id_start,
idx,
);
}
// Superblock
@@ -98,7 +125,7 @@ const SuperblockError = error{
};
/// A squashfs Superblock
pub const Superblock = packed struct(u768) {
pub const Superblock = extern struct {
magic: u32,
inode_count: u32,
mod_time: u32,
@@ -113,7 +140,7 @@ pub const Superblock = packed struct(u768) {
zstd,
},
block_log: u16,
flags: packed struct {
flags: packed struct(u16) {
inode_uncompressed: bool,
data_uncompressed: bool,
check: bool,
+2 -2
View File
@@ -57,7 +57,7 @@ fn decomp(d: ?*const Decompressor, alloc: std.mem.Allocator, in: []u8, out: []u8
inline fn zstdDecomp(buffer: []u8, in: []u8, out: []u8) !usize {
var rdr: Reader = .fixed(in);
var d = zstd.Decompress.init(&rdr, buffer, .{ .window_len = @truncate(in.len) });
var d = zstd.Decompress.init(&rdr, buffer, .{ .window_len = @truncate(out.len) });
return d.reader.readSliceShort(out);
}
@@ -67,7 +67,7 @@ inline fn zstdDecomp(buffer: []u8, in: []u8, out: []u8) !usize {
pub const stateless_decompressor: Decompressor = .{ .decomp_fn = statelessDecomp };
fn statelessDecomp(_: ?*const Decompressor, alloc: std.mem.Allocator, in: []u8, out: []u8) Error!usize {
const buf = try alloc.alloc(u8, in.len + zstd.block_size_max);
const buf = try alloc.alloc(u8, out.len + zstd.block_size_max);
defer alloc.free(buf);
return zstdDecomp(buf, in, out);
}
+67
View File
@@ -0,0 +1,67 @@
const std = @import("std");
const Reader = std.Io.Reader;
const Inode = @import("inode.zig");
const DirEntry = @This();
block_start: u32,
block_offset: u16,
type: Inode.Type,
name: []const u8,
pub fn deinit(self: DirEntry, alloc: std.mem.Allocator) void {
alloc.free(self.name);
}
pub fn readDirectory(alloc: std.mem.Allocator, rdr: *Reader, size: u32) ![]DirEntry {
var hdr: Header = undefined;
var raw: RawEntry = undefined;
var out: std.ArrayList(DirEntry) = try .initCapacity(alloc, 30);
errdefer {
for (out.items) |ent|
alloc.free(ent.name);
out.deinit(alloc);
}
var tot_red: u32 = 3;
while (tot_red < size) {
try rdr.readSliceEndian(Header, @ptrCast(&hdr), .little);
try out.ensureUnusedCapacity(alloc, hdr.count + 1);
tot_red += @sizeOf(Header);
for (hdr.count + 1) |_| {
try rdr.readSliceEndian(RawEntry, @ptrCast(&raw), .little);
const new_name = try alloc.alloc(u8, raw.name_size + 1);
try rdr.readSliceEndian(u8, new_name, .little);
const new = out.addOneAssumeCapacity();
new.* = .{
.block_start = hdr.block_start,
.block_offset = raw.block_offset,
.type = raw.type,
.name = new_name,
};
tot_red += @sizeOf(RawEntry) + raw.name_size + 1;
}
}
return out.toOwnedSlice(alloc);
}
// Types
const Header = extern struct {
count: u32,
block_start: u32,
num: u32,
};
const RawEntry = extern struct {
block_offset: u16,
num_offset: i16,
type: Inode.Type,
name_size: u16,
};
+20 -5
View File
@@ -3,6 +3,8 @@
const std = @import("std");
const Io = std.Io;
const Archive = @import("archive.zig");
const DirEntry = @import("directory.zig");
const ExtractionOptions = @import("options.zig");
const Inode = @import("inode.zig");
@@ -10,17 +12,21 @@ const File = @This();
alloc: std.mem.Allocator,
archive: Archive,
inode: Inode,
name: []const u8,
/// Creates a new File from an inode. Takes ownership of the Inode and creates a copy of the given name.
/// Requires the given allocator was used to create the Inode.
pub fn init(alloc: std.mem.Allocator, in: Inode, name: []const u8) !File {
pub fn init(alloc: std.mem.Allocator, archive: Archive, in: Inode, name: []const u8) !File {
const new_name = try alloc.alloc(u8, name.len);
@memcpy(new_name, name);
return .{
.alloc = alloc,
.archive = archive,
.inode = in,
.name = new_name,
};
@@ -31,10 +37,10 @@ pub fn deinit(self: File) void {
}
pub fn open(self: File, alloc: std.mem.Allocator, io: Io, filepath: []const u8) !File {
_ = self;
_ = alloc;
_ = io;
_ = filepath;
switch (self.inode.hdr.inode_type) {
.dir, .ext_dir => {},
else => return Error.NotDirectory,
}
return error.TODO;
}
@@ -46,3 +52,12 @@ pub fn extract(self: File, alloc: std.mem.Allocator, io: Io, filepath: []const u
_ = options;
return error.TODO;
}
// Types
pub const Error = error{
NotDirectory,
NotRegularFile,
NotSymlink,
NotDevice,
};
+1 -1
View File
@@ -87,7 +87,7 @@ pub const Data = union(Type) {
ext_socket: misc.ExtIPC,
};
pub const Header = packed struct {
pub const Header = extern struct {
inode_type: Type,
permissions: u16,
uid_idx: u16,
+3 -3
View File
@@ -1,6 +1,6 @@
const Reader = @import("std").Io.Reader;
pub const Dir = packed struct {
pub const Dir = extern struct {
block_start: u32,
hard_links: u32,
size: u16,
@@ -14,7 +14,7 @@ pub const Dir = packed struct {
}
};
pub const ExtDir = packed struct {
pub const ExtDir = extern struct {
hard_links: u32,
size: u32,
block_start: u32,
@@ -26,7 +26,7 @@ pub const ExtDir = packed struct {
pub fn read(rdr: *Reader) !ExtDir {
var d: ExtDir = undefined;
try rdr.readSliceEndian(Dir, @ptrCast(&d), .little);
try rdr.readSliceEndian(ExtDir, @ptrCast(&d), .little);
return d;
}
};
+56 -35
View File
@@ -1,34 +1,43 @@
const std = @import("std");
const Reader = std.Io.Reader;
pub const BlockSize = packed struct {
pub const BlockSize = packed struct(u32) {
size: u24,
uncompressed: bool,
_: u7,
};
const FileRawRead = extern struct {
block_start: u32,
frag_idx: u32,
frag_block_offset: u32,
size: u32,
};
pub const File = struct {
block_start: u32, // bytes 0-3
frag_idx: u32, // bytes 4-7
frag_block_offset: u32, // bytes 8-11
size: u32, // bytes 12-15
block_start: u32,
frag_idx: u32,
frag_block_offset: u32,
size: u32,
block_sizes: []BlockSize,
pub fn read(alloc: std.mem.Allocator, rdr: *Reader, block_size: u32) !File {
var start: [16]u8 = undefined;
try rdr.readSliceAll(&start);
const frag_idx: u32 = std.mem.readInt(u32, start[4..8], .little);
const size: u32 = std.mem.readInt(u32, start[12..16], .little);
var num_blocks: u32 = size / block_size;
if (size % block_size != 0 and frag_idx == 0xFFFFFFFF) num_blocks += 1;
var raw: FileRawRead = undefined;
try rdr.readSliceEndian(FileRawRead, @ptrCast(&raw), .little);
var num_blocks: u32 = raw.size / block_size;
if (raw.size % block_size != 0 and raw.frag_idx == 0xFFFFFFFF)
num_blocks += 1;
const sizes = try alloc.alloc(BlockSize, num_blocks);
errdefer alloc.free(sizes);
try rdr.readSliceEndian(BlockSize, sizes, .little);
return .{
.block_start = std.mem.readInt(u32, start[0..4], .little),
.frag_idx = frag_idx,
.frag_block_offset = std.mem.readInt(u32, start[8..12], .little),
.size = size,
.block_start = raw.block_start,
.frag_idx = raw.frag_idx,
.frag_block_offset = raw.frag_block_offset,
.size = raw.size,
.block_sizes = sizes,
};
}
@@ -38,34 +47,46 @@ pub const File = struct {
}
};
const ExtFileRawRead = extern struct {
block_start: u64,
size: u64,
sparse: u64,
hard_links: u32,
frag_idx: u32,
frag_block_offset: u32,
xattr_idx: u32,
};
pub const ExtFile = struct {
block_start: u64, // bytes 0-7
size: u64, // bytes 8-15
sparse: u64, // bytes 16-23
hard_links: u32, // bytes 24-27
frag_idx: u32, // bytes 28-31
frag_block_offset: u32, // bytes 32-35
xattr_idx: u32, // bytes 36-39
block_start: u64,
size: u64,
sparse: u64,
hard_links: u32,
frag_idx: u32,
frag_block_offset: u32,
xattr_idx: u32,
block_sizes: []BlockSize,
pub fn read(alloc: std.mem.Allocator, rdr: *Reader, block_size: u32) !ExtFile {
var start: [40]u8 = undefined;
try rdr.readSliceAll(&start);
const frag_idx: u32 = std.mem.readInt(u32, start[28..32], .little);
const size: u64 = std.mem.readInt(u64, start[8..16], .little);
var num_blocks: u32 = @truncate(size / block_size);
if (size % block_size != 0 and frag_idx == 0xFFFFFFFF) num_blocks += 1;
var raw: ExtFileRawRead = undefined;
try rdr.readSliceEndian(ExtFileRawRead, @ptrCast(&raw), .little);
var num_blocks: u32 = @truncate(raw.size / block_size);
if (raw.size % block_size != 0 and raw.frag_idx == 0xFFFFFFFF)
num_blocks += 1;
const sizes = try alloc.alloc(BlockSize, num_blocks);
errdefer alloc.free(sizes);
try rdr.readSliceEndian(BlockSize, sizes, .little);
return .{
.block_start = std.mem.readInt(u64, start[0..8], .little),
.size = size,
.sparse = std.mem.readInt(u64, start[16..24], .little),
.hard_links = std.mem.readInt(u32, start[24..28], .little),
.frag_idx = frag_idx,
.frag_block_offset = std.mem.readInt(u32, start[32..36], .little),
.xattr_idx = std.mem.readInt(u32, start[36..40], .little),
.block_start = raw.block_start,
.size = raw.size,
.sparse = raw.sparse,
.hard_links = raw.hard_links,
.frag_idx = raw.frag_idx,
.frag_block_offset = raw.frag_block_offset,
.xattr_idx = raw.xattr_idx,
.block_sizes = sizes,
};
}
+4 -4
View File
@@ -50,7 +50,7 @@ pub const ExtSymlink = struct {
};
/// A block or character device.
pub const Dev = packed struct {
pub const Dev = extern struct {
hard_links: u32,
dev: u32,
@@ -62,7 +62,7 @@ pub const Dev = packed struct {
};
/// An extended block or character device.
pub const ExtDev = packed struct {
pub const ExtDev = extern struct {
hard_links: u32,
dev: u32,
xattr_idx: u32,
@@ -75,7 +75,7 @@ pub const ExtDev = packed struct {
};
/// A socket or FIFO file.
pub const IPC = packed struct {
pub const IPC = extern struct {
hard_links: u32,
pub fn read(rdr: *Reader) !IPC {
@@ -86,7 +86,7 @@ pub const IPC = packed struct {
};
/// An extended socket or FIFO file.
pub const ExtIPC = packed struct {
pub const ExtIPC = extern struct {
hard_links: u32,
xattr_idx: u32,
+1 -4
View File
@@ -13,10 +13,7 @@ test "Basics" {
var fil = try Io.Dir.cwd().openFile(io, TestArchive, .{});
defer fil.close(io);
var sfs: Archive = try .init(io, fil, 0);
if (sfs.super != LinuxPATestCorrectSuperblock) {
std.debug.print("Superblock wrong\nShould be: {}\n\nis: {}\n", .{ LinuxPATestCorrectSuperblock, sfs.super });
return error.BadSuperblock;
}
try std.testing.expectEqualDeep(sfs.super, LinuxPATestCorrectSuperblock);
const root_file = try sfs.root(alloc, io);
defer root_file.deinit();
}
+1 -1
View File
@@ -6,7 +6,7 @@ const StreamError = std.Io.Reader.StreamError;
const Decompressor = @import("decompressor.zig");
const BlockHeader = packed struct {
const BlockHeader = packed struct(u16) {
size: u15,
uncompressed: bool,
};