Added basic test

Fixed various bugs
This commit is contained in:
Caleb Gardner
2025-07-17 03:50:09 -05:00
parent 87563e43a5
commit d6b136bc8f
13 changed files with 145 additions and 97 deletions
+1 -1
View File
@@ -119,7 +119,7 @@ pub fn main() !void {
return;
}
const fil = try std.fs.cwd().openFile(filename, .{});
var rdr = squashfs.FileReader.init(
var rdr = squashfs.SfsFile.init(
alloc.allocator(),
fil,
offset,
+5 -5
View File
@@ -17,7 +17,7 @@ const RawEntry = struct {
name: []const u8,
pub fn init(alloc: std.mem.Allocator, rdr: anytype) !RawEntry {
const fixed: [8]u8 = undefined;
var fixed: [8]u8 = undefined;
_ = try rdr.read(&fixed);
const size = std.mem.readInt(u16, fixed[6..8], .little);
const name = try alloc.alloc(u8, size + 1);
@@ -25,7 +25,7 @@ const RawEntry = struct {
return .{
.offset = std.mem.readInt(u16, fixed[0..2], .little),
.num_offset = std.mem.readInt(i16, fixed[2..4], .little),
.type = std.mem.readInt(u16, fixed[4..6], .little),
.type = @enumFromInt(std.mem.readInt(u16, fixed[4..6], .little)),
.size = size,
.name = name,
};
@@ -44,8 +44,8 @@ pub const Entry = struct {
}
};
pub fn readDirectory(alloc: std.mem.Allocator, rdr: anytype, size: u32) []Entry {
const entries: std.ArrayList(Entry) = .init(alloc);
pub fn readDirectory(alloc: std.mem.Allocator, rdr: anytype, size: u32) ![]Entry {
var entries: std.ArrayList(Entry) = .init(alloc);
errdefer entries.deinit();
var cur_red: u32 = 3; // dir size includes "." & "..", so its actual size is off by 3.
var hdr: Header = undefined;
@@ -60,7 +60,7 @@ pub fn readDirectory(alloc: std.mem.Allocator, rdr: anytype, size: u32) []Entry
entries.appendAssumeCapacity(.{
.block = hdr.block,
.offset = raw_ent.offset,
.num = hdr.num + raw_ent.num_offset,
.num = @truncate(@abs(@as(i64, hdr.num) + raw_ent.num_offset)),
.type = raw_ent.type,
.name = raw_ent.name,
});
+44 -12
View File
@@ -7,6 +7,7 @@ const Inode = @import("inode.zig");
const SfsReader = @import("reader.zig").SfsReader;
const ToReader = @import("reader/to_read.zig").ToRead;
const ExtractionOptions = @import("extract_options.zig");
const DataReader = @import("reader/data.zig").DataReader;
const Compression = @import("superblock.zig").Compression;
const MetadataReader = @import("reader/metadata.zig").MetadataReader;
@@ -27,7 +28,7 @@ pub fn File(comptime T: type) type {
/// Directory entries. Only populated on directories.
entries: ?[]DirEntry = null,
/// File reader. Only populated on regular files.
// data_reader: ?DataReader
data_reader: ?DataReader(T) = null,
pub fn init(rdr: *SfsReader(T), inode: Inode, name: []const u8) !Self {
var out = Self{
@@ -37,33 +38,60 @@ pub fn File(comptime T: type) type {
};
switch (inode.data) {
.dir => |d| {
const meta = MetadataReader(T).init(
var meta = MetadataReader(T).init(
rdr.alloc,
rdr.super.comp,
rdr.rdr,
d.block + rdr.super.dir_start,
);
try meta.skip(d.offset);
out.entries = try dir.readDirectory(rdr.alloc, meta, d.size);
out.entries = try dir.readDirectory(rdr.alloc, &meta, d.size);
},
.ext_dir => |d| {
const meta = MetadataReader(T).init(
var meta = MetadataReader(T).init(
rdr.alloc,
rdr.super.comp,
rdr.rdr,
d.block + rdr.super.dir_start,
);
try meta.skip(d.offset);
out.entries = try dir.readDirectory(rdr.alloc, meta, d.size);
out.entries = try dir.readDirectory(rdr.alloc, &meta, d.size);
},
.file => |f| {
_ = f;
//TODO
out.data_reader = try .init(
rdr.alloc,
rdr.rdr,
rdr.super.comp,
f.block,
f.size,
f.block_sizes,
rdr.super.block_size,
);
if (f.hasFragment()) {
try out.data_reader.?.addFragment(
try rdr.frag_table.get(f.frag_idx),
f.frag_offset,
);
}
},
.ext_file => |f| {
_ = f;
//TODO
out.data_reader = try .init(
rdr.alloc,
rdr.rdr,
rdr.super.comp,
f.block,
f.size,
f.block_sizes,
rdr.super.block_size,
);
if (f.hasFragment()) {
try out.data_reader.?.addFragment(
try rdr.frag_table.get(f.frag_idx),
f.frag_offset,
);
}
},
else => {},
}
return out;
}
@@ -76,9 +104,13 @@ pub fn File(comptime T: type) type {
}
self.rdr.alloc.free(self.entries.?);
}
// if(self.data_reader != null){
// self.data_reader.?.deinit();
// }
if (self.data_reader != null) {
self.data_reader.?.deinit();
}
}
pub fn iter(self: Self) !void {
_ = self;
}
};
}
+15 -21
View File
@@ -4,12 +4,6 @@ const dir = @import("inode/dir.zig");
const file = @import("inode/file.zig");
const misc = @import("inode/misc.zig");
const Reader = @import("reader.zig");
const DirEntry = @import("directory.zig").Entry;
const ToRead = @import("reader/to_read.zig").ToRead;
const Compression = @import("superblock.zig").Compression;
const MetadataReader = @import("reader/metadata.zig").MetadataReader;
pub const Ref = packed struct {
offset: u16,
block: u32,
@@ -67,21 +61,21 @@ data: Data,
pub fn init(rdr: anytype, alloc: std.mem.Allocator, block_size: u32) !Self {
var hdr: Header = undefined;
_ = try rdr.read(std.mem.asBytes(&hdr));
const data = switch (hdr.type) {
.dir => .{ .dir = .init(rdr) },
.file => .{ .file = .init(rdr, alloc, block_size) },
.symlink => .{ .symlink = .init(rdr, alloc) },
.block_dev => .{ .block_dev = .init(rdr) },
.char_dev => .{ .char_dev = .init(rdr) },
.fifo => .{ .fifo = .init(rdr) },
.socket => .{ .socket = .init(rdr) },
.ext_dir => .{ .ext_dir = .init(rdr) },
.ext_file => .{ .ext_file = .init(rdr, alloc, block_size) },
.ext_symlink => .{ .ext_symlink = .init(rdr, alloc) },
.ext_block_dev => .{ .ext_block_dev = .init(rdr) },
.ext_char_dev => .{ .ext_char_dev = .init(rdr) },
.ext_fifo => .{ .ext_fifo = .init(rdr) },
.ext_socket => .{ .ext_socket = .init(rdr) },
const data: Data = switch (hdr.type) {
.dir => .{ .dir = try .init(rdr) },
.file => .{ .file = try .init(rdr, alloc, block_size) },
.symlink => .{ .symlink = try .init(rdr, alloc) },
.block_dev => .{ .block_dev = try .init(rdr) },
.char_dev => .{ .char_dev = try .init(rdr) },
.fifo => .{ .fifo = try .init(rdr) },
.socket => .{ .socket = try .init(rdr) },
.ext_dir => .{ .ext_dir = try .init(rdr) },
.ext_file => .{ .ext_file = try .init(rdr, alloc, block_size) },
.ext_symlink => .{ .ext_symlink = try .init(rdr, alloc) },
.ext_block_dev => .{ .ext_block_dev = try .init(rdr) },
.ext_char_dev => .{ .ext_char_dev = try .init(rdr) },
.ext_fifo => .{ .ext_fifo = try .init(rdr) },
.ext_socket => .{ .ext_socket = try .init(rdr) },
};
return .{
.hdr = hdr,
+4 -4
View File
@@ -8,8 +8,8 @@ pub const Dir = packed struct {
parent_num: u32,
pub fn init(rdr: anytype) !Dir {
const out: Dir = undefined;
_ = rdr.read(std.mem.asBytes(&out));
var out: Dir = undefined;
_ = try rdr.read(std.mem.asBytes(&out));
return out;
}
};
@@ -24,8 +24,8 @@ pub const ExtDir = packed struct {
xattr_idx: u32,
pub fn init(rdr: anytype) !ExtDir {
const out: ExtDir = undefined;
_ = rdr.read(std.mem.asBytes(&out));
var out: ExtDir = undefined;
_ = try rdr.read(std.mem.asBytes(&out));
return out;
}
};
+14 -8
View File
@@ -9,7 +9,7 @@ pub const BlockSize = packed struct {
pub const File = struct {
block: u32,
frag_idx: u32,
offset: u32,
frag_offset: u32,
size: u32,
block_sizes: []BlockSize,
@@ -22,17 +22,20 @@ pub const File = struct {
if (size % block_size > 0 and frag_idx != 0xffffffff) {
blocks += 1;
}
const block_sizes = alloc.alloc(BlockSize, blocks);
const block_sizes = try alloc.alloc(BlockSize, blocks);
errdefer alloc.free(block_sizes);
_ = try rdr.read(std.mem.sliceAsBytes(block_sizes));
return .{
.block = std.mem.readInt(u32, fixed[0..4], .little),
.frag_idx = frag_idx,
.offset = std.mem.readInt(u32, fixed[8..12], .little),
.frag_offset = std.mem.readInt(u32, fixed[8..12], .little),
.size = size,
.block_sizes = block_sizes,
};
}
pub fn hasFragment(self: File) bool {
return self.frag_idx != 0xffffffff;
}
};
pub const ExtFile = struct {
@@ -41,7 +44,7 @@ pub const ExtFile = struct {
sparse: u64,
hard_link: u32,
frag_idx: u32,
offset: u32,
frag_offset: u32,
xattr_idx: u32,
block_sizes: []BlockSize,
@@ -50,11 +53,11 @@ pub const ExtFile = struct {
_ = try rdr.read(&fixed);
const size = std.mem.readInt(u64, fixed[8..16], .little);
const frag_idx = std.mem.readInt(u32, fixed[28..32], .little);
var blocks: u32 = size / block_size;
var blocks: u32 = @truncate(size / block_size);
if (size % block_size > 0 and frag_idx != 0xffffffff) {
blocks += 1;
}
const block_sizes = alloc.alloc(BlockSize, blocks);
const block_sizes = try alloc.alloc(BlockSize, blocks);
errdefer alloc.free(block_sizes);
_ = try rdr.read(std.mem.sliceAsBytes(block_sizes));
return .{
@@ -63,9 +66,12 @@ pub const ExtFile = struct {
.sparse = std.mem.readInt(u64, fixed[16..24], .little),
.hard_link = std.mem.readInt(u32, fixed[24..28], .little),
.frag_idx = frag_idx,
.offset = std.mem.readInt(u32, fixed[32..36], .little),
.frag_offset = std.mem.readInt(u32, fixed[32..36], .little),
.xattr_idx = std.mem.readInt(u32, fixed[36..40], .little),
.block_sizes = blocks,
.block_sizes = block_sizes,
};
}
pub fn hasFragment(self: ExtFile) bool {
return self.frag_idx != 0xffffffff;
}
};
+6 -6
View File
@@ -9,7 +9,7 @@ pub const Symlink = struct {
var fixed: [8]u8 = undefined;
_ = try rdr.read(&fixed);
const size = std.mem.readInt(u32, fixed[4..8], .little);
const target = alloc.alloc(u8, size);
const target = try alloc.alloc(u8, size);
errdefer alloc.free(target);
_ = try rdr.read(target);
return .{
@@ -29,7 +29,7 @@ pub const ExtSymlink = struct {
var fixed: [8]u8 = undefined;
_ = try rdr.read(&fixed);
const size = std.mem.readInt(u32, fixed[4..8], .little);
const target = alloc.alloc(u8, size);
const target = try alloc.alloc(u8, size);
errdefer alloc.free(target);
_ = try rdr.read(target);
var xattr_idx: u32 = 0;
@@ -47,7 +47,7 @@ pub const Dev = packed struct {
device: u32,
pub fn init(rdr: anytype) !Dev {
const out: Dev = undefined;
var out: Dev = undefined;
_ = try rdr.read(std.mem.asBytes(&out));
return out;
}
@@ -59,7 +59,7 @@ pub const ExtDev = packed struct {
xattr_idx: u32,
pub fn init(rdr: anytype) !ExtDev {
const out: ExtDev = undefined;
var out: ExtDev = undefined;
_ = try rdr.read(std.mem.asBytes(&out));
return out;
}
@@ -69,7 +69,7 @@ pub const IPC = packed struct {
hard_link: u32,
pub fn init(rdr: anytype) !IPC {
const out: IPC = undefined;
var out: IPC = undefined;
_ = try rdr.read(std.mem.asBytes(&out));
return out;
}
@@ -80,7 +80,7 @@ pub const ExtIPC = packed struct {
xattr_idx: u32,
pub fn init(rdr: anytype) !ExtIPC {
const out: ExtIPC = undefined;
var out: ExtIPC = undefined;
_ = try rdr.read(std.mem.asBytes(&out));
return out;
}
+7 -15
View File
@@ -29,7 +29,6 @@ pub fn SfsReader(comptime T: type) type {
/// Export table. Each element is an inode referce.
/// If accessing directly, keep in mind, the table starts at inode 1, as such it's recommended to use the InodeAt function instead.
export_table: Table(Inode.Ref, T) = undefined,
root: ?File(T) = null,
pub fn init(alloc: std.mem.Allocator, rdr: T, offset: u64) !Self {
var out: Self = .{
@@ -46,28 +45,21 @@ pub fn SfsReader(comptime T: type) type {
self.id_table.deinit();
self.frag_table.deinit();
self.export_table.deinit();
if (self.root != null) self.root.?.deinit();
}
fn populateRoot(self: *Self) !void {
if (self.root != null) return;
const meta = MetadataReader(T).init(
pub fn archiveRoot(self: *Self) !File(T) {
var meta = MetadataReader(T).init(
self.alloc,
self.super.comp,
self.rdr,
self.super.inode_start + self.super.root_ref.block,
);
try meta.skip(self.super.root_ref.offset);
const root_inode: Inode = try .init(meta, self.alloc, self.super.block_size);
self.root = try .init(self, root_inode, "");
const root_inode: Inode = try .init(&meta, self.alloc, self.super.block_size);
return try .init(self, root_inode, "");
}
pub fn archiveRoot(self: *Self) !File {
if (self.root == null) try self.populateRoot();
return self.root.?;
}
pub fn open(self: *Self, path: []const u8) !File {
if (self.root == null) try self.populateRoot();
pub fn open(self: *Self, path: []const u8) !File(T) {
_ = self;
_ = path;
// return self.root.?.open(path);
return error{TODO}.TODO;
@@ -78,7 +70,7 @@ pub fn SfsReader(comptime T: type) type {
pub fn inodeAt(self: Self, num: u32) !Inode {
if (!self.super.flags.has_export) return SfsError.NotExportable;
const ref = try self.export_table.get(num - 1);
const meta = MetadataReader(T).init(
var meta = MetadataReader(T).init(
self.alloc,
self.super.comp,
self.rdr,
+5 -11
View File
@@ -27,8 +27,8 @@ pub fn DataReader(comptime T: type) type {
frag: []u8 = &[0]u8{},
read_block: []u8,
read_offset: u64,
read_block: []u8 = &[0]u8{},
read_offset: u64 = 0,
read_idx: u32 = 0,
pub fn init(
@@ -41,7 +41,7 @@ pub fn DataReader(comptime T: type) type {
block_size: u32,
) !Self {
var cur_offset = init_offset;
const offsets = alloc.alloc(u64, sizes.len);
const offsets = try alloc.alloc(u64, sizes.len);
for (0..sizes.len) |i| {
offsets[i] = cur_offset;
cur_offset += sizes[i].size;
@@ -74,10 +74,7 @@ pub fn DataReader(comptime T: type) type {
defer self.alloc.free(block);
_ = try self.comp.decompress(
self.alloc,
std.io.limitedReader(
self.rdr.readerAt(entry.block),
entry.size.size,
),
self.rdr.readerAt(entry.block).reader(),
block,
);
@memcpy(self.frag, block[offset..]);
@@ -106,10 +103,7 @@ pub fn DataReader(comptime T: type) type {
}
_ = try self.comp.decompress(
self.alloc,
std.io.limitedReader(
self.rdr.readerAt(self.offsets[idx]),
self.sizes[idx].size,
),
self.rdr.readerAt(self.offsets[idx]).reader(),
block,
);
return block;
+8 -8
View File
@@ -32,12 +32,12 @@ pub fn MetadataReader(comptime T: type) type {
}
fn readNextBlock(self: *Self) !void {
const hdr: MetaHeader = undefined;
_ = try self.rdr.pread(std.mem.asBytes(hdr), self.offset);
var hdr: MetaHeader = undefined;
_ = try self.rdr.pread(std.mem.asBytes(&hdr), self.offset);
self.offset += 2;
self.block_size = try self.comp.decompress(
self.alloc,
std.io.limitedReader(self.rdr.readerAt(self.offset), hdr.size),
self.rdr.readerAt(self.offset).reader(),
&self.block,
);
self.offset += hdr.size;
@@ -45,14 +45,14 @@ pub fn MetadataReader(comptime T: type) type {
}
pub fn skip(self: *Self, offset: u32) !void {
var skipped = 0;
const hdr: MetaHeader = undefined;
var skipped: u32 = 0;
var hdr: MetaHeader = undefined;
while (offset - skipped >= 8192) {
_ = try self.rdr.pread(std.mem.asBytes(hdr), self.offset);
_ = try self.rdr.pread(std.mem.asBytes(&hdr), self.offset);
self.offset += 2 + hdr.size;
skipped += 8192;
}
var to_skip = 0;
var to_skip: u32 = 0;
while (skipped < offset) {
if (self.block_offset >= self.block_size) try self.readNextBlock();
to_skip = @min(self.block_size - self.block_offset, offset - skipped);
@@ -69,7 +69,7 @@ pub fn MetadataReader(comptime T: type) type {
to_read = @min(buf.len - cur_red, self.block_size - self.block_offset);
@memcpy(buf[cur_red .. cur_red + to_read], self.block[self.block_offset .. self.block_offset + to_read]);
cur_red += to_read;
self.block_offset += to_read;
self.block_offset += @truncate(to_read);
}
return cur_red;
}
+18
View File
@@ -5,6 +5,8 @@ pub fn ToRead(comptime T: type) type {
return struct {
const Self = @This();
pub const Error = anyerror;
rdr: T,
offset: u64,
@@ -20,5 +22,21 @@ pub fn ToRead(comptime T: type) type {
self.offset += red;
return red;
}
pub fn readAll(self: *Self, buf: []u8) !usize {
var cur_red = try self.read(buf);
if (cur_red == 0) return cur_red;
var res: usize = 0;
while (cur_red < buf.len) {
res = try self.read(buf[cur_red..]);
if (res == 0) break;
cur_red += res;
}
return cur_red;
}
pub fn reader(self: anytype) std.io.Reader(*Self, anyerror, read) {
return .{
.context = @constCast(self),
};
}
};
}
+13 -1
View File
@@ -3,4 +3,16 @@ const std = @import("std");
pub const SfsReader = @import("reader.zig").SfsReader;
pub const ExtractionOptions = @import("extract_options.zig");
pub const FileReader = SfsReader(std.fs.File);
pub const SfsFile = SfsReader(std.fs.File);
const test_file = "testing/LinuxPATest.sfs";
test "OpenTest" {
const fil = try std.fs.cwd().openFile(test_file, .{});
defer fil.close();
var rdr: SfsFile = try .init(std.testing.allocator, fil, 0);
defer rdr.deinit();
std.debug.print("{}\n", .{rdr.super});
const root = try rdr.archiveRoot();
defer root.deinit();
}
+5 -5
View File
@@ -54,24 +54,24 @@ pub const Compression = enum(u16) {
pub fn decompress(self: Compression, alloc: std.mem.Allocator, source: anytype, dest: []u8) !usize {
switch (self) {
.gzip => {
const decomp = std.compress.zlib.decompressor(source);
var decomp = std.compress.zlib.decompressor(source);
return decomp.read(dest);
},
.lzma => {
const decomp = try std.compress.lzma.decompress(alloc, source);
var decomp = try std.compress.lzma.decompress(alloc, source);
defer decomp.deinit();
return decomp.read(dest);
},
.lzo => return DecompressError.LzoUnavailable,
.xz => {
const decomp = try std.compress.xz.decompress(alloc, source);
var decomp = try std.compress.xz.decompress(alloc, source);
defer decomp.deinit();
return decomp.read(dest);
},
.lz4 => return DecompressError.Lz4Unavailable,
.zstd => {
const window: [@min(std.compress.zstd.DecompressorOptions.default_window_buffer_len, dest.len)]u8 = undefined;
const decomp = std.compress.zstd.decompressor(source, .{ .window_buffer = window });
var window: [std.compress.zstd.DecompressorOptions.default_window_buffer_len]u8 = undefined;
var decomp = std.compress.zstd.decompressor(source, .{ .window_buffer = &window });
return decomp.read(dest);
},
}