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": { "build": {
"command": "zig", "command": "zig",
"args": ["build", "-Duse_c_libs=true", "-Ddebug=true"], "args": ["build", "-Ddebug=true"],
}, },
"program": "zig-out/bin/unsquashfs", "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.block_size,
self.super.root_ref, self.super.root_ref,
); );
return .init(alloc, root_inode, ""); return .init(alloc, self, root_inode, "");
} }
/// Opens a File within the archive. /// Opens a File within the archive.
pub fn open(self: Archive, alloc: std.mem.Allocator, io: Io, filepath: []const u8) !File { 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. /// 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 { pub fn extract(self: Archive, alloc: std.mem.Allocator, io: Io, extract_dir: []const u8, options: ExtractionOptions) !void {
_ = self; const root_inode = try Utils.inodeFromRef(
_ = alloc; alloc,
_ = io; io,
self.file,
&self.stateless_decomp,
self.super.inode_start,
self.super.block_size,
self.super.root_ref,
);
_ = root_inode;
_ = extract_dir; _ = extract_dir;
_ = options; _ = options;
return error.TODO; 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 { pub fn inode(self: Archive, alloc: std.mem.Allocator, io: Io, num: u32) !Inode {
if (!self.super.flags.exportable) if (!self.super.flags.exportable)
return error.NotExportable; 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( return Utils.inodeFromRef(
alloc, alloc,
io, io,
@@ -85,6 +100,18 @@ pub fn inode(self: Archive, alloc: std.mem.Allocator, io: Io, num: u32) !Inode {
ref, 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 // Superblock
@@ -98,7 +125,7 @@ const SuperblockError = error{
}; };
/// A squashfs Superblock /// A squashfs Superblock
pub const Superblock = packed struct(u768) { pub const Superblock = extern struct {
magic: u32, magic: u32,
inode_count: u32, inode_count: u32,
mod_time: u32, mod_time: u32,
@@ -113,7 +140,7 @@ pub const Superblock = packed struct(u768) {
zstd, zstd,
}, },
block_log: u16, block_log: u16,
flags: packed struct { flags: packed struct(u16) {
inode_uncompressed: bool, inode_uncompressed: bool,
data_uncompressed: bool, data_uncompressed: bool,
check: 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 { inline fn zstdDecomp(buffer: []u8, in: []u8, out: []u8) !usize {
var rdr: Reader = .fixed(in); 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); 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 }; pub const stateless_decompressor: Decompressor = .{ .decomp_fn = statelessDecomp };
fn statelessDecomp(_: ?*const Decompressor, alloc: std.mem.Allocator, in: []u8, out: []u8) Error!usize { 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); defer alloc.free(buf);
return zstdDecomp(buf, in, out); 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 std = @import("std");
const Io = std.Io; const Io = std.Io;
const Archive = @import("archive.zig");
const DirEntry = @import("directory.zig");
const ExtractionOptions = @import("options.zig"); const ExtractionOptions = @import("options.zig");
const Inode = @import("inode.zig"); const Inode = @import("inode.zig");
@@ -10,17 +12,21 @@ const File = @This();
alloc: std.mem.Allocator, alloc: std.mem.Allocator,
archive: Archive,
inode: Inode, inode: Inode,
name: []const u8, name: []const u8,
/// Creates a new File from an inode. Takes ownership of the Inode and creates a copy of the given name. /// 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. /// 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); const new_name = try alloc.alloc(u8, name.len);
@memcpy(new_name, name); @memcpy(new_name, name);
return .{ return .{
.alloc = alloc, .alloc = alloc,
.archive = archive,
.inode = in, .inode = in,
.name = new_name, .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 { pub fn open(self: File, alloc: std.mem.Allocator, io: Io, filepath: []const u8) !File {
_ = self; switch (self.inode.hdr.inode_type) {
_ = alloc; .dir, .ext_dir => {},
_ = io; else => return Error.NotDirectory,
_ = filepath; }
return error.TODO; return error.TODO;
} }
@@ -46,3 +52,12 @@ pub fn extract(self: File, alloc: std.mem.Allocator, io: Io, filepath: []const u
_ = options; _ = options;
return error.TODO; 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, ext_socket: misc.ExtIPC,
}; };
pub const Header = packed struct { pub const Header = extern struct {
inode_type: Type, inode_type: Type,
permissions: u16, permissions: u16,
uid_idx: u16, uid_idx: u16,
+3 -3
View File
@@ -1,6 +1,6 @@
const Reader = @import("std").Io.Reader; const Reader = @import("std").Io.Reader;
pub const Dir = packed struct { pub const Dir = extern struct {
block_start: u32, block_start: u32,
hard_links: u32, hard_links: u32,
size: u16, size: u16,
@@ -14,7 +14,7 @@ pub const Dir = packed struct {
} }
}; };
pub const ExtDir = packed struct { pub const ExtDir = extern struct {
hard_links: u32, hard_links: u32,
size: u32, size: u32,
block_start: u32, block_start: u32,
@@ -26,7 +26,7 @@ pub const ExtDir = packed struct {
pub fn read(rdr: *Reader) !ExtDir { pub fn read(rdr: *Reader) !ExtDir {
var d: ExtDir = undefined; var d: ExtDir = undefined;
try rdr.readSliceEndian(Dir, @ptrCast(&d), .little); try rdr.readSliceEndian(ExtDir, @ptrCast(&d), .little);
return d; return d;
} }
}; };
+56 -35
View File
@@ -1,34 +1,43 @@
const std = @import("std"); const std = @import("std");
const Reader = std.Io.Reader; const Reader = std.Io.Reader;
pub const BlockSize = packed struct { pub const BlockSize = packed struct(u32) {
size: u24, size: u24,
uncompressed: bool, uncompressed: bool,
_: u7, _: u7,
}; };
const FileRawRead = extern struct {
block_start: u32,
frag_idx: u32,
frag_block_offset: u32,
size: u32,
};
pub const File = struct { pub const File = struct {
block_start: u32, // bytes 0-3 block_start: u32,
frag_idx: u32, // bytes 4-7 frag_idx: u32,
frag_block_offset: u32, // bytes 8-11 frag_block_offset: u32,
size: u32, // bytes 12-15 size: u32,
block_sizes: []BlockSize, block_sizes: []BlockSize,
pub fn read(alloc: std.mem.Allocator, rdr: *Reader, block_size: u32) !File { pub fn read(alloc: std.mem.Allocator, rdr: *Reader, block_size: u32) !File {
var start: [16]u8 = undefined; var raw: FileRawRead = undefined;
try rdr.readSliceAll(&start); try rdr.readSliceEndian(FileRawRead, @ptrCast(&raw), .little);
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 = raw.size / block_size;
var num_blocks: u32 = size / block_size; if (raw.size % block_size != 0 and raw.frag_idx == 0xFFFFFFFF)
if (size % block_size != 0 and frag_idx == 0xFFFFFFFF) num_blocks += 1; num_blocks += 1;
const sizes = try alloc.alloc(BlockSize, num_blocks); const sizes = try alloc.alloc(BlockSize, num_blocks);
errdefer alloc.free(sizes); errdefer alloc.free(sizes);
try rdr.readSliceEndian(BlockSize, sizes, .little); try rdr.readSliceEndian(BlockSize, sizes, .little);
return .{ return .{
.block_start = std.mem.readInt(u32, start[0..4], .little), .block_start = raw.block_start,
.frag_idx = frag_idx, .frag_idx = raw.frag_idx,
.frag_block_offset = std.mem.readInt(u32, start[8..12], .little), .frag_block_offset = raw.frag_block_offset,
.size = size, .size = raw.size,
.block_sizes = sizes, .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 { pub const ExtFile = struct {
block_start: u64, // bytes 0-7 block_start: u64,
size: u64, // bytes 8-15 size: u64,
sparse: u64, // bytes 16-23 sparse: u64,
hard_links: u32, // bytes 24-27 hard_links: u32,
frag_idx: u32, // bytes 28-31 frag_idx: u32,
frag_block_offset: u32, // bytes 32-35 frag_block_offset: u32,
xattr_idx: u32, // bytes 36-39 xattr_idx: u32,
block_sizes: []BlockSize, block_sizes: []BlockSize,
pub fn read(alloc: std.mem.Allocator, rdr: *Reader, block_size: u32) !ExtFile { pub fn read(alloc: std.mem.Allocator, rdr: *Reader, block_size: u32) !ExtFile {
var start: [40]u8 = undefined; var raw: ExtFileRawRead = undefined;
try rdr.readSliceAll(&start); try rdr.readSliceEndian(ExtFileRawRead, @ptrCast(&raw), .little);
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(raw.size / block_size);
var num_blocks: u32 = @truncate(size / block_size); if (raw.size % block_size != 0 and raw.frag_idx == 0xFFFFFFFF)
if (size % block_size != 0 and frag_idx == 0xFFFFFFFF) num_blocks += 1; num_blocks += 1;
const sizes = try alloc.alloc(BlockSize, num_blocks); const sizes = try alloc.alloc(BlockSize, num_blocks);
errdefer alloc.free(sizes); errdefer alloc.free(sizes);
try rdr.readSliceEndian(BlockSize, sizes, .little); try rdr.readSliceEndian(BlockSize, sizes, .little);
return .{ return .{
.block_start = std.mem.readInt(u64, start[0..8], .little), .block_start = raw.block_start,
.size = size, .size = raw.size,
.sparse = std.mem.readInt(u64, start[16..24], .little), .sparse = raw.sparse,
.hard_links = std.mem.readInt(u32, start[24..28], .little), .hard_links = raw.hard_links,
.frag_idx = frag_idx, .frag_idx = raw.frag_idx,
.frag_block_offset = std.mem.readInt(u32, start[32..36], .little), .frag_block_offset = raw.frag_block_offset,
.xattr_idx = std.mem.readInt(u32, start[36..40], .little), .xattr_idx = raw.xattr_idx,
.block_sizes = sizes, .block_sizes = sizes,
}; };
} }
+4 -4
View File
@@ -50,7 +50,7 @@ pub const ExtSymlink = struct {
}; };
/// A block or character device. /// A block or character device.
pub const Dev = packed struct { pub const Dev = extern struct {
hard_links: u32, hard_links: u32,
dev: u32, dev: u32,
@@ -62,7 +62,7 @@ pub const Dev = packed struct {
}; };
/// An extended block or character device. /// An extended block or character device.
pub const ExtDev = packed struct { pub const ExtDev = extern struct {
hard_links: u32, hard_links: u32,
dev: u32, dev: u32,
xattr_idx: u32, xattr_idx: u32,
@@ -75,7 +75,7 @@ pub const ExtDev = packed struct {
}; };
/// A socket or FIFO file. /// A socket or FIFO file.
pub const IPC = packed struct { pub const IPC = extern struct {
hard_links: u32, hard_links: u32,
pub fn read(rdr: *Reader) !IPC { pub fn read(rdr: *Reader) !IPC {
@@ -86,7 +86,7 @@ pub const IPC = packed struct {
}; };
/// An extended socket or FIFO file. /// An extended socket or FIFO file.
pub const ExtIPC = packed struct { pub const ExtIPC = extern struct {
hard_links: u32, hard_links: u32,
xattr_idx: u32, xattr_idx: u32,
+1 -4
View File
@@ -13,10 +13,7 @@ test "Basics" {
var fil = try Io.Dir.cwd().openFile(io, TestArchive, .{}); var fil = try Io.Dir.cwd().openFile(io, TestArchive, .{});
defer fil.close(io); defer fil.close(io);
var sfs: Archive = try .init(io, fil, 0); var sfs: Archive = try .init(io, fil, 0);
if (sfs.super != LinuxPATestCorrectSuperblock) { try std.testing.expectEqualDeep(sfs.super, LinuxPATestCorrectSuperblock);
std.debug.print("Superblock wrong\nShould be: {}\n\nis: {}\n", .{ LinuxPATestCorrectSuperblock, sfs.super });
return error.BadSuperblock;
}
const root_file = try sfs.root(alloc, io); const root_file = try sfs.root(alloc, io);
defer root_file.deinit(); defer root_file.deinit();
} }
+1 -1
View File
@@ -6,7 +6,7 @@ const StreamError = std.Io.Reader.StreamError;
const Decompressor = @import("decompressor.zig"); const Decompressor = @import("decompressor.zig");
const BlockHeader = packed struct { const BlockHeader = packed struct(u16) {
size: u15, size: u15,
uncompressed: bool, uncompressed: bool,
}; };