From 3684a958a049d9c59bcde3e970bd7a4d859ada7e Mon Sep 17 00:00:00 2001 From: Caleb Gardner Date: Wed, 14 May 2025 01:59:19 -0500 Subject: [PATCH] Almost kind of working --- decompress.zig | 45 ++++++++++++++++++++++++++++++++++++ inode.zig | 10 ++++---- inode_types/dir.zig | 32 +++++++++++++------------- inode_types/file.zig | 12 +++++----- inode_types/sym.zig | 16 ++++++------- metadata_reader.zig | 55 +++++++++++++++++++++++++++++++++----------- squashfs.zig | 17 +++++++++----- superblock.zig | 12 ++-------- test_squashfs.zig | 2 +- 9 files changed, 135 insertions(+), 66 deletions(-) create mode 100644 decompress.zig diff --git a/decompress.zig b/decompress.zig new file mode 100644 index 0000000..962afb6 --- /dev/null +++ b/decompress.zig @@ -0,0 +1,45 @@ +const std = @import("std"); +const compress = std.compress; + +const DecompressError = error{ + LzoNotSupported, + Lz4NotSupported, +}; + +pub const CompressionType = enum(u16) { + gzip = 1, + lzma, + lzo, + xz, + lz4, + zstd, + + pub fn Decompress(self: CompressionType, alloc: std.mem.Allocator, rdr: std.io.AnyReader) ![]u8 { + var out = std.ArrayList(u8).init(alloc); + defer out.deinit(); + switch (self) { + .gzip => try compress.zlib.decompress(rdr, out.writer()), + .lzma => { + var decomp = try compress.lzma.decompress(alloc, rdr); + defer decomp.deinit(); + try decomp.reader().readAllArrayList(&out, 1024 * 1024); + }, + .lzo => return DecompressError.LzoNotSupported, + .xz => { + var decomp = try compress.xz.decompress(alloc, rdr); + defer decomp.deinit(); + try decomp.reader().readAllArrayList(&out, 1024 * 1024); + }, + .lz4 => return DecompressError.Lz4NotSupported, + .zstd => { + const buf = try alloc.alloc(u8, compress.zstd.DecompressorOptions.default_window_buffer_len); + defer alloc.free(buf); + var decomp = compress.zstd.decompressor(rdr, .{ + .window_buffer = buf, + }); + try decomp.reader().readAllArrayList(&out, 1024 * 1024); + }, + } + return try out.toOwnedSlice(); + } +}; diff --git a/inode.zig b/inode.zig index 7af5e93..c565ad8 100644 --- a/inode.zig +++ b/inode.zig @@ -45,8 +45,8 @@ pub const InodeData = union(enum) { ext_symlink: sym.ExtSymlinkInode, ext_block_device: misc.ExtDeviceInode, ext_char_device: misc.ExtDeviceInode, - ext_fifo: misc.IPCInode, - ext_socket: misc.IPCInode, + ext_fifo: misc.ExtIPCInode, + ext_socket: misc.ExtIPCInode, }; const dir = @import("inode_types/dir.zig"); @@ -61,13 +61,13 @@ pub const Inode = struct { const io = @import("std").io; -pub fn readInode(rdr: io.AnyReader, block_size: u64, alloc: std.heap.Allocator) !Inode { +pub fn readInode(rdr: io.AnyReader, block_size: u32, alloc: std.mem.Allocator) !Inode { const hdr = try rdr.readStruct(InodeHeader); return Inode{ .header = hdr, .data = switch (hdr.inode_type) { .dir => .{ - .dir = dir.readDirInode(rdr), + .dir = try dir.readDirInode(rdr), }, .ext_dir => .{ .ext_dir = try dir.readExtDirInode(rdr, alloc), @@ -79,7 +79,7 @@ pub fn readInode(rdr: io.AnyReader, block_size: u64, alloc: std.heap.Allocator) .ext_file = try file.readExtFileInode(rdr, block_size, alloc), }, .symlink => .{ - .block_device = try misc.readSymlinkInode(rdr, alloc), + .symlink = try sym.readSymlinkInode(rdr, alloc), }, .ext_symlink => .{ .ext_symlink = try sym.readExtSymlinkInode(rdr, alloc), diff --git a/inode_types/dir.zig b/inode_types/dir.zig index e9b7cfe..4dada6e 100644 --- a/inode_types/dir.zig +++ b/inode_types/dir.zig @@ -17,18 +17,18 @@ pub const DirIndex = struct { dir_header_offset: u32, dir_table_offset: u32, name_size: u32, - name: []const u8, + name: []u8, }; -fn readDirIndex(rdr: io.AnyReader, alloc: std.heap.Allocator) !DirIndex { - const out = DirIndex{ +fn readDirIndex(rdr: io.AnyReader, alloc: std.mem.Allocator) !DirIndex { + var out = DirIndex{ .dir_header_offset = try rdr.readInt(u32, std.builtin.Endian.little), .dir_table_offset = try rdr.readInt(u32, std.builtin.Endian.little), .name_size = try rdr.readInt(u32, std.builtin.Endian.little), .name = undefined, }; out.name = try alloc.alloc(u8, out.name_size); - try rdr.read(out.name); + _ = try rdr.readAll(out.name); return out; } @@ -40,23 +40,23 @@ pub const ExtDirInode = struct { dir_index_count: u16, dir_block_offset: u16, xattr_index: u32, - indexes: []const DirIndex, + indexes: []DirIndex, }; -pub fn readExtDirInode(rdr: io.AnyReader, alloc: std.heap.Allocator) !ExtDirInode { - const out = ExtDirInode{ - .hard_links = rdr.readInt(u32, std.builtin.Endian.little), - .dir_table_size = rdr.readInt(u32, std.builtin.Endian.little), - .dir_block_start = rdr.readInt(u32, std.builtin.Endian.little), - .parent_inode_num = rdr.readInt(u32, std.builtin.Endian.little), - .dir_index_count = rdr.readInt(u16, std.builtin.Endian.little), - .dir_block_offset = rdr.readInt(u16, std.builtin.Endian.little), - .xattr_index = rdr.readInt(u32, std.builtin.Endian.little), +pub fn readExtDirInode(rdr: io.AnyReader, alloc: std.mem.Allocator) !ExtDirInode { + var out = ExtDirInode{ + .hard_links = try rdr.readInt(u32, std.builtin.Endian.little), + .dir_table_size = try rdr.readInt(u32, std.builtin.Endian.little), + .dir_block_start = try rdr.readInt(u32, std.builtin.Endian.little), + .parent_inode_num = try rdr.readInt(u32, std.builtin.Endian.little), + .dir_index_count = try rdr.readInt(u16, std.builtin.Endian.little), + .dir_block_offset = try rdr.readInt(u16, std.builtin.Endian.little), + .xattr_index = try rdr.readInt(u32, std.builtin.Endian.little), .indexes = undefined, }; - const tmp = std.ArrayList(DirIndex).init(alloc); + var tmp = std.ArrayList(DirIndex).init(alloc); try tmp.resize(out.dir_index_count); - const i: u16 = 0; + var i: u16 = 0; while (i < out.dir_index_count) : (i += 1) { tmp.items[i] = try readDirIndex(rdr, alloc); } diff --git a/inode_types/file.zig b/inode_types/file.zig index e5407cd..53e3923 100644 --- a/inode_types/file.zig +++ b/inode_types/file.zig @@ -8,8 +8,8 @@ pub const FileInode = struct { block_sizes: []const u32, }; -pub fn readFileInode(rdr: std.io.AnyReader, block_size: u32, alloc: std.heap.Allocator) !FileInode { - const out = FileInode{ +pub fn readFileInode(rdr: std.io.AnyReader, block_size: u32, alloc: std.mem.Allocator) !FileInode { + var out = FileInode{ .start = try rdr.readInt(u32, std.builtin.Endian.little), .frag_index = try rdr.readInt(u32, std.builtin.Endian.little), .frag_block_offset = try rdr.readInt(u32, std.builtin.Endian.little), @@ -21,7 +21,7 @@ pub fn readFileInode(rdr: std.io.AnyReader, block_size: u32, alloc: std.heap.All block_num += 1; } out.block_sizes = try alloc.alloc(u32, block_num); - try rdr.read(std.mem.asBytes(&out.block_sizes)); + _ = try rdr.readAll(std.mem.asBytes(&out.block_sizes)); return out; } @@ -36,8 +36,8 @@ pub const ExtFileInode = struct { block_sizes: []const u32, }; -pub fn readExtFileInode(rdr: std.io.AnyReader, block_size: u32, alloc: std.heap.Allocator) !ExtFileInode { - const out = ExtFileInode{ +pub fn readExtFileInode(rdr: std.io.AnyReader, block_size: u32, alloc: std.mem.Allocator) !ExtFileInode { + var out = ExtFileInode{ .start = try rdr.readInt(u64, std.builtin.Endian.little), .size = try rdr.readInt(u64, std.builtin.Endian.little), .sparse = try rdr.readInt(u64, std.builtin.Endian.little), @@ -52,6 +52,6 @@ pub fn readExtFileInode(rdr: std.io.AnyReader, block_size: u32, alloc: std.heap. block_num += 1; } out.block_sizes = try alloc.alloc(u32, block_num); - try rdr.read(std.mem.asBytes(&out.block_sizes)); + _ = try rdr.readAll(std.mem.asBytes(&out.block_sizes)); return out; } diff --git a/inode_types/sym.zig b/inode_types/sym.zig index fd9a44a..1c5d2b7 100644 --- a/inode_types/sym.zig +++ b/inode_types/sym.zig @@ -4,36 +4,36 @@ const io = std.io; pub const SymlinkInode = struct { hard_links: u32, target_size: u32, - path: []const u8, + path: []u8, }; -pub fn readSymlinkInode(rdr: io.AnyReader, alloc: std.heap.Allocator) !SymlinkInode { - const out = SymlinkInode{ +pub fn readSymlinkInode(rdr: io.AnyReader, alloc: std.mem.Allocator) !SymlinkInode { + var out = SymlinkInode{ .hard_links = try rdr.readInt(u32, std.builtin.Endian.little), .target_size = try rdr.readInt(u32, std.builtin.Endian.little), .path = undefined, }; out.path = try alloc.alloc(u8, out.target_size + 1); - try rdr.read(out.path); + _ = try rdr.readAll(out.path); return out; } pub const ExtSymlinkInode = struct { hard_links: u32, target_size: u32, - path: []const u8, + path: []u8, xattr_index: u32, }; -pub fn readExtSymlinkInode(rdr: io.AnyReader, alloc: std.heap.Allocator) !SymlinkInode { - const out = ExtSymlinkInode{ +pub fn readExtSymlinkInode(rdr: io.AnyReader, alloc: std.mem.Allocator) !ExtSymlinkInode { + var out = ExtSymlinkInode{ .hard_links = try rdr.readInt(u32, std.builtin.Endian.little), .target_size = try rdr.readInt(u32, std.builtin.Endian.little), .path = undefined, .xattr_index = undefined, }; out.path = try alloc.alloc(u8, out.target_size + 1); - try rdr.read(out.path); + _ = try rdr.readAll(out.path); out.xattr_index = try rdr.readInt(u32, std.builtin.Endian.little); return out; } diff --git a/metadata_reader.zig b/metadata_reader.zig index 9d4b597..7689854 100644 --- a/metadata_reader.zig +++ b/metadata_reader.zig @@ -1,45 +1,72 @@ const std = @import("std"); const io = std.io; +const CompressionType = @import("decompress.zig").CompressionType; + const MetadataHeader = packed struct { not_compressed: bool, size: u15, }; -const MetadataReader = struct { +pub const MetadataReader = struct { rdr: std.io.AnyReader, - alloc: std.heap.Allocator, - curBlock: []const u8, + alloc: std.mem.Allocator, + decomp: CompressionType, + curBlock: []u8, curOffset: u16, - pub fn init(rdr: io.AnyReader, alloc: std.heap.Allocator) !MetadataReader { - const out = .{ + pub fn init(decomp: CompressionType, rdr: io.AnyReader, alloc: std.mem.Allocator) !MetadataReader { + var out: MetadataReader = .{ .rdr = rdr, .alloc = alloc, - .curBlock = undefined, + .decomp = decomp, + .curBlock = &[_]u8{}, .curOffset = 0, }; try out.readNextBlock(); return out; } - pub fn any(self: MetadataReader) !io.AnyReader { + pub fn any(self: *MetadataReader) io.AnyReader { return .{ - .context = @ptrCast(&self), - .readFn = typeErasedReadFn, + .context = @ptrCast(self), + .readFn = readOpaque, }; } - fn readNextBlock(self: MetadataReader) !void { - if (self.curBlock != undefined) { + fn readNextBlock(self: *MetadataReader) !void { + if (self.curBlock.len != 0) { self.alloc.free(self.curBlock); } self.curOffset = 0; const hdr = try self.rdr.readStruct(MetadataHeader); - const buf = try self.alloc.alloc(u8, hdr.size); if (hdr.not_compressed) { - self.curBlock = buf; + self.curBlock = try self.alloc.alloc(u8, hdr.size); + _ = try self.rdr.readAll(self.curBlock); } else { - //TODO: decompress + var limit_rdr = std.io.limitedReader(self.rdr, hdr.size); + self.curBlock = try self.decomp.Decompress(self.alloc, limit_rdr.reader().any()); } } + + pub fn read(self: *MetadataReader, bytes: []u8) anyerror!usize { + var cur_read: usize = 0; + var to_read: usize = 0; + while (cur_read < bytes.len) { + std.debug.print("hello there!, {}\n", .{cur_read}); + if (self.curOffset + 1 == self.curBlock.len) { + try self.readNextBlock(); + } + to_read = @min(bytes.len - cur_read, self.curBlock.len - self.curOffset); + std.debug.print("{} {} {}\n", .{ cur_read, to_read, self.curOffset }); + std.mem.copyForwards(u8, bytes[cur_read..], self.curBlock[self.curOffset .. self.curOffset + to_read]); + self.curOffset += @truncate(to_read); + cur_read += to_read; + } + return cur_read; + } + + fn readOpaque(context: *const anyopaque, bytes: []u8) anyerror!usize { + var self: *MetadataReader = @constCast(@ptrCast(@alignCast(context))); + return self.read(bytes); + } }; diff --git a/squashfs.zig b/squashfs.zig index 93c75ad..df990fd 100644 --- a/squashfs.zig +++ b/squashfs.zig @@ -2,30 +2,35 @@ const std = @import("std"); const fs = std.fs; const Superblock = @import("superblock.zig").Superblock; -const Inode = @import("inode.zig").Inode; +const inode = @import("inode.zig"); +const MetadataReader = @import("metadata_reader.zig").MetadataReader; pub const Reader = struct { super: Superblock, rdr: fs.File, - root: Inode, + root: inode.Inode, alloc: std.heap.GeneralPurposeAllocator(.{}), - pub fn close(self: Reader) void { + pub fn close(self: *Reader) void { self.rdr.close(); - self.alloc.deinit(); + _ = self.alloc.deinit(); } }; pub fn newReader(filename: []const u8) !Reader { const file = try std.fs.cwd().openFile(filename, .{}); errdefer file.close(); - const alloc = std.heap.GeneralPurposeAllocator(.{}).init(); - errdefer alloc.deinit(); + var alloc: std.heap.GeneralPurposeAllocator(.{}) = .init; + errdefer _ = alloc.deinit(); const super = try file.reader().readStruct(Superblock); try super.valid(); + try file.seekTo(super.inode_table + super.root_inode.block_start); + var root_reader: MetadataReader = try .init(super.comp, file.reader().any(), alloc.allocator()); + const root_inode = try inode.readInode(root_reader.any(), super.block_size, alloc.allocator()); return Reader{ .super = super, .rdr = file, + .root = root_inode, .alloc = alloc, }; } diff --git a/superblock.zig b/superblock.zig index 2834083..1ae96f7 100644 --- a/superblock.zig +++ b/superblock.zig @@ -1,5 +1,6 @@ const math = @import("std").math; const InodeRef = @import("inode.zig").InodeRef; +const CompressionType = @import("decompress.zig").CompressionType; pub const SuperblockError = error{ InvalidMagic, @@ -7,22 +8,13 @@ pub const SuperblockError = error{ InvalidVersion, }; -pub const CompressionType = enum(u16) { - gzip = 1, - lzma, - lzo, - xz, - lz4, - zstd, -}; - pub const Superblock = packed struct { magic: u32, count: u32, mod_time: u32, block_size: u32, frags: u32, - comp: u16, + comp: CompressionType, block_log: u16, flags: packed struct { inode_uncomp: bool, diff --git a/test_squashfs.zig b/test_squashfs.zig index 1b4cd0e..e472e01 100644 --- a/test_squashfs.zig +++ b/test_squashfs.zig @@ -5,6 +5,6 @@ const squashfs = @import("squashfs.zig"); const testFileName = "testing/LinuxPATest.sfs"; test "open test file" { - const reader = try squashfs.newReader(testFileName); + var reader = try squashfs.newReader(testFileName); defer reader.close(); }