From 986f308c601dce122ac58129cd17afc55d054ebd Mon Sep 17 00:00:00 2001 From: Caleb Gardner Date: Fri, 16 May 2025 05:55:40 -0500 Subject: [PATCH] Start over to clear my head --- src/decompress.zig | 37 ++++++------ src/directory.zig | 81 -------------------------- src/file.zig | 95 +----------------------------- src/file_offset_reader.zig | 31 ---------- src/inode.zig | 113 ------------------------------------ src/inode/inode.zig | 36 ++++++++++++ src/inode_types/dir.zig | 63 -------------------- src/inode_types/file.zig | 57 ------------------ src/inode_types/misc.zig | 21 ------- src/inode_types/sym.zig | 39 ------------- src/metadata_reader.zig | 83 -------------------------- src/reader.zig | 32 ++++++++++ src/readers/file_holder.zig | 48 +++++++++++++++ src/readers/metadata.zig | 70 ++++++++++++++++++++++ src/root.zig | 2 +- src/squashfs.zig | 54 ----------------- src/superblock.zig | 45 +++++--------- src/test_squashfs.zig | 13 ----- 18 files changed, 221 insertions(+), 699 deletions(-) delete mode 100644 src/directory.zig delete mode 100644 src/file_offset_reader.zig delete mode 100644 src/inode.zig create mode 100644 src/inode/inode.zig delete mode 100644 src/inode_types/dir.zig delete mode 100644 src/inode_types/file.zig delete mode 100644 src/inode_types/misc.zig delete mode 100644 src/inode_types/sym.zig delete mode 100644 src/metadata_reader.zig create mode 100644 src/reader.zig create mode 100644 src/readers/file_holder.zig create mode 100644 src/readers/metadata.zig delete mode 100644 src/squashfs.zig delete mode 100644 src/test_squashfs.zig diff --git a/src/decompress.zig b/src/decompress.zig index 253cd44..b23a614 100644 --- a/src/decompress.zig +++ b/src/decompress.zig @@ -1,45 +1,44 @@ const std = @import("std"); -const compress = std.compress; +const io = std.io; const DecompressError = error{ - LzoNotSupported, - Lz4NotSupported, + LzoUnsupported, + Lz4Unsupported, }; -pub const CompressionType = enum(u16) { - gzip = 1, +pub const DecompressType = enum(u16) { + zlib = 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(); + pub fn decompress(self: DecompressType, alloc: std.mem.Allocator, in: io.AnyReader) !std.ArrayList(u8) { + const out: std.ArrayList(u8) = .init(alloc); switch (self) { - .gzip => try compress.zlib.decompress(rdr, out.writer()), + .zlib => try std.compress.zlib.decompress(in, out), .lzma => { - var decomp = try compress.lzma.decompress(alloc, rdr); + const decomp = try std.compress.lzma.decompress(alloc, in); defer decomp.deinit(); - try decomp.reader().readAllArrayList(&out, 1024 * 1024); + try decomp.reader().readAllArrayList(&out, 1048576); }, - .lzo => return DecompressError.LzoNotSupported, + .lzo => return DecompressError.LzoUnsupported, .xz => { - var decomp = try compress.xz.decompress(alloc, rdr); + const decomp = try std.compress.xz.decompress(alloc, in); defer decomp.deinit(); - try decomp.reader().readAllArrayList(&out, 1024 * 1024); + try decomp.reader().readAllArrayList(&out, 1048576); }, - .lz4 => return DecompressError.Lz4NotSupported, + .lz4 => return DecompressError.Lz4Unsupported, .zstd => { - const buf = try alloc.alloc(u8, compress.zstd.DecompressorOptions.default_window_buffer_len); + const buf = try alloc.alloc(u8, std.compress.zstd.DecompressorOptions.default_window_buffer_len); defer alloc.free(buf); - var decomp = compress.zstd.decompressor(rdr, .{ + const decomp = std.compress.zstd.decompressor(in, .{ .window_buffer = buf, }); - try decomp.reader().readAllArrayList(&out, 1024 * 1024); + try decomp.reader().readAllArrayList(&out, 1048576); }, } - return try out.toOwnedSlice(); + return out; } }; diff --git a/src/directory.zig b/src/directory.zig deleted file mode 100644 index 30fec13..0000000 --- a/src/directory.zig +++ /dev/null @@ -1,81 +0,0 @@ -const std = @import("std"); - -const CompressionType = @import("decompress.zig").CompressionType; - -const DirHeader = packed struct { - count: u32, - inode_block_start: u32, - inode_num: u32, -}; - -const RawDirEntry = struct { - inode_offset: u16, - inode_num_difference: i16, - inode_type: u16, - name_size: u16, - name: []u8, - - fn init(rdr: std.io.AnyReader, alloc: std.mem.Allocator) !RawDirEntry { - var out: RawDirEntry = .{ - .inode_offset = try rdr.readInt(u16, std.builtin.Endian.little), - .inode_num_difference = try rdr.readInt(i16, std.builtin.Endian.little), - .inode_type = try rdr.readInt(u16, std.builtin.Endian.little), - .name_size = try rdr.readInt(u16, std.builtin.Endian.little), - .name = undefined, - }; - out.name = try alloc.alloc(u8, out.name_size); - _ = try rdr.readAll(out.name); - return out; - } -}; - -pub const DirEntry = struct { - inode_offset: u16, - inode_block_start: u32, - inode_num: u32, - name: []u8, - - fn init(raw: RawDirEntry, hdr: DirHeader) DirEntry { - return .{ - .inode_offset = raw.inode_offset, - .inode_block_start = hdr.inode_block_start, - .inode_num = hdr.inode_num + @as(u32, @intCast(raw.inode_num_difference)), - .name = raw.name, - }; - } -}; - -const MetadataHeader = @import("metadata_reader.zig").MetadataHeader; - -pub fn readDirEntries(alloc: std.mem.Allocator, comp: CompressionType, rdr: std.io.AnyReader, size: u32) ![]DirEntry { - var total_size: u32 = 3; - var meta_hdr: MetadataHeader = undefined; - var dir_hdr: DirHeader = undefined; - var buf: []u8 = undefined; - defer alloc.free(buf); - var buf_rdr: std.io.FixedBufferStream([]u8) = undefined; - var i: u32 = 0; - var entries: std.ArrayList(DirEntry) = .init(alloc); - var raw: RawDirEntry = undefined; - while (total_size < size) { - meta_hdr = try rdr.readStruct(MetadataHeader); - if (meta_hdr.not_compressed) { - buf = try alloc.realloc(buf, meta_hdr.size); - _ = try rdr.readAll(buf); - } else { - alloc.free(buf); - var limit = std.io.limitedReader(rdr, meta_hdr.size); - buf = try comp.decompress(alloc, limit.reader().any()); - } - buf_rdr = std.io.fixedBufferStream(buf); - dir_hdr = try buf_rdr.reader().readStruct(DirHeader); - total_size += 12; - i = 0; - while (i < dir_hdr.count) : (i += 1) { - raw = try .init(buf_rdr.reader().any(), alloc); - total_size += @sizeOf(RawDirEntry); - try entries.append(.init(raw, dir_hdr)); - } - } - return try entries.toOwnedSlice(); -} diff --git a/src/file.zig b/src/file.zig index a447b71..2a0e42d 100644 --- a/src/file.zig +++ b/src/file.zig @@ -1,94 +1 @@ -const std = @import("std"); - -const inode = @import("inode.zig"); -const Reader = @import("squashfs.zig").Reader; -const MetadataReader = @import("metadata_reader.zig").MetadataReader; -const FileOffsetReader = @import("file_offset_reader.zig").FileOffsetReader; -const dir = @import("directory.zig"); - -const FileOpenError = error{ - NotFound, - NotDirectory, -}; - -pub const File = struct { - rdr: *Reader, - inode: inode.Inode, - name: []const u8, - dir_entries: []dir.DirEntry = &[0]dir.DirEntry{}, - - pub fn fromRef(rdr: *Reader, ref: inode.InodeRef, name: []const u8) !File { - var offset_rdr: FileOffsetReader = .init(rdr.rdr, rdr.super.inode_table + ref.block_start); - var meta_rdr: MetadataReader = .init(rdr.super.comp, offset_rdr.any(), rdr.alloc.allocator()); - try meta_rdr.skip(ref.offset); - const in = try inode.readInode(meta_rdr, rdr.super.block_size, rdr.alloc.allocator()); - return .{ - .rdr = rdr, - .inode = in, - .name = name, - }; - } - - pub fn fromDirEntry(rdr: *Reader, ent: dir.DirEntry) !File { - var offset_rdr: FileOffsetReader = .init(&rdr.rdr, rdr.super.inode_table + ent.inode_block_start); - var meta_rdr: MetadataReader = try .init( - rdr.super.comp, - offset_rdr.any(), - rdr.alloc.allocator(), - ); - try meta_rdr.skip(ent.inode_offset); - const in = try inode.readInode(meta_rdr.any(), rdr.super.block_size, rdr.alloc.allocator()); - return .{ - .rdr = rdr, - .inode = in, - .name = ent.name, - }; - } - - pub fn open(self: *File, path: []const u8) (anyerror || FileOpenError)!File { - if (path.len == 0) return self.*; - const clean_path: []const u8 = std.mem.trimLeft(u8, path, "/"); - if (clean_path.len == 0 or std.mem.eql(u8, clean_path, ".")) { - return self.*; - } - switch (self.inode.header.inode_type) { - .dir, .ext_dir => {}, - else => return FileOpenError.NotDirectory, - } - try self.readDirEntries(); - const file_name = std.mem.sliceTo(clean_path, '/'); - for (self.dir_entries) |ent| { - std.debug.print("yo {}\n", .{ent}); - if (std.mem.eql(u8, file_name, ent.name)) { - return try File.fromDirEntry(self.rdr, ent); - } - } - return FileOpenError.NotFound; - } - - fn readDirEntries(self: *File) (anyerror || FileOpenError)!void { - if (self.dir_entries.len != 0) { - return; - } - var dir_block_offset: u32 = undefined; - var dir_block_start: u32 = undefined; - var size: u32 = undefined; - switch (self.inode.data) { - .dir => |d| { - dir_block_start = d.dir_block_start; - dir_block_offset = d.dir_block_offset; - size = d.dir_table_size; - }, - .ext_dir => |d| { - dir_block_start = d.dir_block_start; - dir_block_offset = d.dir_block_offset; - size = d.dir_table_size; - }, - else => return FileOpenError.NotDirectory, - } - std.debug.print("{}\n", .{self.rdr}); - var offset_rdr: FileOffsetReader = .init(&self.rdr.rdr, self.rdr.super.dir_table + dir_block_start); - var meta_rdr: MetadataReader = try .init(self.rdr.super.comp, offset_rdr.any(), self.rdr.alloc.allocator()); - self.dir_entries = try dir.readDirEntries(self.rdr.alloc.allocator(), self.rdr.super.comp, meta_rdr.any(), size); - } -}; +pub const File = struct {}; //TODO diff --git a/src/file_offset_reader.zig b/src/file_offset_reader.zig deleted file mode 100644 index 9978c9f..0000000 --- a/src/file_offset_reader.zig +++ /dev/null @@ -1,31 +0,0 @@ -const std = @import("std"); - -pub const FileOffsetReader = struct { - file: *std.fs.File, - offset: u64, - - pub fn init(file: *std.fs.File, initial_offset: u64) FileOffsetReader { - return .{ - .file = file, - .offset = initial_offset, - }; - } - - pub fn read(self: *FileOffsetReader, bytes: []u8) anyerror!usize { - const red = try self.file.preadAll(bytes, self.offset); - self.offset += @intCast(red); - return red; - } - - pub fn any(self: *FileOffsetReader) std.io.AnyReader { - return .{ - .context = @ptrCast(self), - .readFn = readOpaque, - }; - } - - fn readOpaque(context: *const anyopaque, buf: []u8) anyerror!usize { - var self: *FileOffsetReader = @constCast(@ptrCast(@alignCast(context))); - return self.read(buf); - } -}; diff --git a/src/inode.zig b/src/inode.zig deleted file mode 100644 index 35f9036..0000000 --- a/src/inode.zig +++ /dev/null @@ -1,113 +0,0 @@ -const std = @import("std"); - -pub const InodeRef = packed struct { - offset: u16, - block_start: u32, - _: u16, -}; - -pub const InodeType = enum(u16) { - dir = 1, - file, - symlink, - block_device, - char_device, - fifo, - socket, - ext_dir, - ext_file, - ext_symlink, - ext_block_device, - ext_char_device, - ext_fifo, - ext_socket, -}; - -pub const InodeHeader = packed struct { - inode_type: InodeType, - perm: u16, - uid_index: u16, - gid_index: u16, - mod_time: u32, - inode_num: u32, -}; - -pub const InodeData = union(enum) { - dir: dir.DirInode, - file: file.FileInode, - symlink: sym.SymlinkInode, - block_device: misc.DeviceInode, - char_device: misc.DeviceInode, - fifo: misc.IPCInode, - socket: misc.IPCInode, - ext_dir: dir.ExtDirInode, - ext_file: file.ExtFileInode, - ext_symlink: sym.ExtSymlinkInode, - ext_block_device: misc.ExtDeviceInode, - ext_char_device: misc.ExtDeviceInode, - ext_fifo: misc.ExtIPCInode, - ext_socket: misc.ExtIPCInode, -}; - -const dir = @import("inode_types/dir.zig"); -const file = @import("inode_types/file.zig"); -const sym = @import("inode_types/sym.zig"); -const misc = @import("inode_types/misc.zig"); - -pub const Inode = struct { - header: InodeHeader, - data: InodeData, -}; - -const io = @import("std").io; - -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 = try .init(rdr), - }, - .ext_dir => .{ - .ext_dir = try .init(rdr, alloc), - }, - .file => .{ - .file = try .init(rdr, block_size, alloc), - }, - .ext_file => .{ - .ext_file = try .init(rdr, block_size, alloc), - }, - .symlink => .{ - .symlink = try .init(rdr, alloc), - }, - .ext_symlink => .{ - .ext_symlink = try .init(rdr, alloc), - }, - .block_device => .{ - .block_device = try rdr.readStruct(misc.DeviceInode), - }, - .ext_block_device => .{ - .ext_block_device = try rdr.readStruct(misc.ExtDeviceInode), - }, - .char_device => .{ - .char_device = try rdr.readStruct(misc.DeviceInode), - }, - .ext_char_device => .{ - .ext_char_device = try rdr.readStruct(misc.ExtDeviceInode), - }, - .fifo => .{ - .fifo = try rdr.readStruct(misc.IPCInode), - }, - .ext_fifo => .{ - .ext_fifo = try rdr.readStruct(misc.ExtIPCInode), - }, - .socket => .{ - .socket = try rdr.readStruct(misc.IPCInode), - }, - .ext_socket => .{ - .ext_socket = try rdr.readStruct(misc.ExtIPCInode), - }, - }, - }; -} diff --git a/src/inode/inode.zig b/src/inode/inode.zig new file mode 100644 index 0000000..0cc04d0 --- /dev/null +++ b/src/inode/inode.zig @@ -0,0 +1,36 @@ +pub const InodeRef = packed struct { + offset: u16, + block_start: u32, + _: u16, +}; + +pub const InodeType = enum(u16) { + dir, + file, + sym, + block, + char, + fifo, + sock, + ext_dir, + ext_file, + ext_sym, + ext_block, + ext_char, + ext_fifo, + ext_sock, +}; + +pub const InodeHeader = packed struct { + inode_type: InodeType, + perm: u16, + uid_idx: u16, + gid_idx: u16, + mod_time: u32, + num: u32, +}; + +pub const Inode = struct { + header: InodeHeader, + data: void, //TODO +}; diff --git a/src/inode_types/dir.zig b/src/inode_types/dir.zig deleted file mode 100644 index 2e00cda..0000000 --- a/src/inode_types/dir.zig +++ /dev/null @@ -1,63 +0,0 @@ -const std = @import("std"); -const io = std.io; - -pub const DirInode = packed struct { - dir_block_start: u32, - hard_links: u32, - dir_table_size: u16, - dir_block_offset: u16, - parent_inode_num: u32, - - pub fn init(rdr: io.AnyReader) !DirInode { - return try rdr.readStruct(DirInode); - } -}; - -pub const DirIndex = struct { - dir_header_offset: u32, - dir_table_offset: u32, - name_size: u32, - name: []u8, - - pub fn init(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.readAll(out.name); - return out; - } -}; - -pub const ExtDirInode = struct { - hard_links: u32, - dir_table_size: u32, - dir_block_start: u32, - parent_inode_num: u32, - dir_index_count: u16, - dir_block_offset: u16, - xattr_index: u32, - indexes: []DirIndex, - - pub fn init(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, - }; - out.indexes = try alloc.alloc(DirIndex, out.dir_index_count); - var i: u16 = 0; - while (i < out.dir_index_count) : (i += 1) { - out.indexes[i] = try .init(rdr, alloc); - } - return out; - } -}; diff --git a/src/inode_types/file.zig b/src/inode_types/file.zig deleted file mode 100644 index c5811e9..0000000 --- a/src/inode_types/file.zig +++ /dev/null @@ -1,57 +0,0 @@ -const std = @import("std"); - -pub const FileInode = struct { - start: u32, - frag_index: u32, - frag_block_offset: u32, - size: u32, - block_sizes: []const u32, - - pub fn init(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), - .size = try rdr.readInt(u32, std.builtin.Endian.little), - .block_sizes = undefined, - }; - var block_num = out.size / block_size; - if (out.frag_index != 0xFFFFFFFF) { - block_num += 1; - } - out.block_sizes = try alloc.alloc(u32, block_num); - _ = try rdr.readAll(std.mem.asBytes(&out.block_sizes)); - return out; - } -}; - -pub const ExtFileInode = struct { - start: u64, - size: u64, - sparse: u64, - hard_links: u32, - frag_index: u32, - frag_block_offset: u32, - xattr_index: u32, - block_sizes: []const u32, - - pub fn init(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), - .hard_links = 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), - .xattr_index = try rdr.readInt(u32, std.builtin.Endian.little), - .block_sizes = undefined, - }; - var block_num = out.size / block_size; - if (out.frag_index != 0xFFFFFFFF and out.size % block_size != 0) { - block_num += 1; - } - out.block_sizes = try alloc.alloc(u32, block_num); - _ = try rdr.readAll(std.mem.asBytes(&out.block_sizes)); - return out; - } -}; diff --git a/src/inode_types/misc.zig b/src/inode_types/misc.zig deleted file mode 100644 index 71cfef1..0000000 --- a/src/inode_types/misc.zig +++ /dev/null @@ -1,21 +0,0 @@ -const io = @import("std").io; - -pub const DeviceInode = packed struct { - hard_links: u32, - device: u32, -}; - -pub const ExtDeviceInode = packed struct { - hard_links: u32, - device: u32, - xattr_index: u32, -}; - -pub const IPCInode = packed struct { - hard_links: u32, -}; - -pub const ExtIPCInode = packed struct { - hard_links: u32, - xattr_index: u32, -}; diff --git a/src/inode_types/sym.zig b/src/inode_types/sym.zig deleted file mode 100644 index 3ff7acd..0000000 --- a/src/inode_types/sym.zig +++ /dev/null @@ -1,39 +0,0 @@ -const std = @import("std"); -const io = std.io; - -pub const SymlinkInode = struct { - hard_links: u32, - target_size: u32, - path: []u8, - - pub fn init(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.readAll(out.path); - return out; - } -}; - -pub const ExtSymlinkInode = struct { - hard_links: u32, - target_size: u32, - path: []u8, - xattr_index: u32, - - pub fn init(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.readAll(out.path); - out.xattr_index = try rdr.readInt(u32, std.builtin.Endian.little); - return out; - } -}; diff --git a/src/metadata_reader.zig b/src/metadata_reader.zig deleted file mode 100644 index 01c9ba8..0000000 --- a/src/metadata_reader.zig +++ /dev/null @@ -1,83 +0,0 @@ -const std = @import("std"); -const io = std.io; - -const CompressionType = @import("decompress.zig").CompressionType; - -pub const MetadataHeader = packed struct { - size: u15, - not_compressed: bool, -}; - -pub const MetadataReader = struct { - rdr: std.io.AnyReader, - alloc: std.mem.Allocator, - decomp: CompressionType, - curBlock: []u8, - curOffset: u16, - - pub fn init(decomp: CompressionType, rdr: io.AnyReader, alloc: std.mem.Allocator) !MetadataReader { - var out: MetadataReader = .{ - .rdr = rdr, - .alloc = alloc, - .decomp = decomp, - .curBlock = &[_]u8{}, - .curOffset = 0, - }; - try out.readNextBlock(); - return out; - } - pub fn deinit(self: *MetadataReader) void { - self.alloc.free(self.curBlock); - } - pub fn any(self: *MetadataReader) io.AnyReader { - return .{ - .context = @ptrCast(self), - .readFn = readOpaque, - }; - } - pub fn skip(self: *MetadataReader, offset: u16) !void { - var to_skip = offset; - var cur_left = self.curBlock.len - self.curOffset; - while (to_skip > cur_left) { - to_skip -= @intCast(cur_left); - try self.readNextBlock(); - cur_left = self.curBlock.len; - } - self.curOffset = to_skip; - } - - 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); - if (hdr.not_compressed) { - self.curBlock = try self.alloc.alloc(u8, hdr.size); - _ = try self.rdr.readAll(self.curBlock); - } else { - 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) { - if (self.curOffset + 1 == self.curBlock.len) { - try self.readNextBlock(); - } - to_read = @min(bytes.len - cur_read, self.curBlock.len - 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/src/reader.zig b/src/reader.zig new file mode 100644 index 0000000..fc21dd6 --- /dev/null +++ b/src/reader.zig @@ -0,0 +1,32 @@ +const std = @import("std"); + +const FileHolder = @import("readers/file_holder.zig").FileHolder; +const Superblock = @import("superblock.zig").Superblock; + +pub const Reader = struct { + arena: std.heap.ArenaAllocator, + holder: FileHolder, + super: Superblock, + + pub fn init(alloc: std.mem.Allocator, filepath: []const u8, offset: u64) !Reader { + var holder: FileHolder = try .init(filepath, offset); + const super: Superblock = try holder.anyAt(0).readStruct(Superblock); + try super.validate(); + const arena: std.heap.ArenaAllocator = .init(alloc); + return .{ + .arena = arena, + .holder = holder, + .super = super, + }; + } + pub fn deinit(self: *const Reader) void { + self.arena.deinit(); + self.holder.deinit(); + } +}; + +test "reader" { + const test_file_path = "testing/LinuxPATest.sfs"; + const rdr: Reader = try .init(std.testing.allocator, test_file_path, 0); + defer rdr.deinit(); +} diff --git a/src/readers/file_holder.zig b/src/readers/file_holder.zig new file mode 100644 index 0000000..da248b1 --- /dev/null +++ b/src/readers/file_holder.zig @@ -0,0 +1,48 @@ +const std = @import("std"); +const fs = std.fs; +const io = std.io; + +pub const FileHolder = struct { + file: fs.File, + offset: u64, + + pub fn init(path: []const u8, offset: u64) !FileHolder { + const fil = try fs.cwd().openFile(path, .{}); + return .{ + .file = fil, + .offset = offset, + }; + } + pub fn deinit(self: FileHolder) void { + self.file.close(); + } + + pub fn anyAt(self: FileHolder, offset: u64) io.AnyReader { + var offsetRdr = FileOffsetReader{ + .file = self.file, + .offset = self.offset + offset, + }; + return offsetRdr.any(); + } +}; + +const FileOffsetReader = struct { + file: fs.File, + offset: u64, + + fn read(self: *FileOffsetReader, bytes: []u8) !usize { + const red = try self.file.pread(bytes, self.offset); + self.offset += red; + return red; + } + fn any(self: *FileOffsetReader) io.AnyReader { + return .{ + .context = @ptrCast(self), + .readFn = readOpaque, + }; + } + fn readOpaque(context: *const anyopaque, bytes: []u8) !usize { + var rdr: *FileOffsetReader = @constCast(@ptrCast(@alignCast(context))); + return try rdr.read(bytes); + } +}; diff --git a/src/readers/metadata.zig b/src/readers/metadata.zig new file mode 100644 index 0000000..a7322a3 --- /dev/null +++ b/src/readers/metadata.zig @@ -0,0 +1,70 @@ +const std = @import("std"); +const io = std.io; + +const DecompressType = @import("../decompress.zig").DecompressType; + +const MetadataHeader = packed struct { + size: u15, + not_compressed: bool, +}; + +pub const MetadataReader = struct { + alloc: std.mem.Allocator, + reader: io.AnyReader, + block: []u8, + decomp: DecompressType, + offset: u32, + + pub fn init(alloc: std.mem.Allocator, rdr: io.AnyReader, decomp: DecompressType) !MetadataReader { + var out: MetadataReader = .{ + .alloc = alloc, + .reader = rdr, + .block = &[0]u8{}, + .decomp = decomp, + .offset = 0, + }; + try out.readNextBlock(); + return out; + } + pub fn deinit(self: *MetadataReader) void { + self.alloc.free(self.block); + } + + fn readNextBlock(self: *MetadataReader) !void { + self.offset = 0; + if (self.block.len > 0) self.alloc.free(self.block); + const hdr = try self.reader.readStruct(MetadataHeader); + if (hdr.not_compressed) { + self.block = try self.alloc.alloc(u8, hdr.size); + _ = try self.reader.readAll(self.block); + } else { + const limit = std.io.limitedReader(self.reader, hdr.size); + const dat = try self.decomp.decompress(self.alloc, limit.reader().any()); + self.block = dat.toOwnedSlice(); + } + } + + pub fn any(self: *MetadataReader) io.AnyReader { + return .{ + .context = @ptrCast(self), + .readFn = readOpaque, + }; + } + + pub fn read(self: *MetadataReader, bytes: []u8) !usize { + var cur_read: usize = 0; + var to_read: usize = 0; + while (cur_read < bytes.len) { + if (self.offset >= self.block.len) try self.readNextBlock(); + to_read = @min(bytes.len - cur_read, self.block.len - self.offset); + std.mem.copyForwards(u8, bytes[cur_read..], self.block[self.offset .. @as(usize, self.offset) + to_read]); + self.offset += @truncate(to_read); + cur_read += to_read; + } + return cur_read; + } + fn readOpaque(context: *const anyopaque, bytes: []u8) !usize { + var rdr: *MetadataReader = @constCast(@ptrCast(@alignCast(context))); + return rdr.read(bytes); + } +}; diff --git a/src/root.zig b/src/root.zig index 9e4b986..fc03aa5 100644 --- a/src/root.zig +++ b/src/root.zig @@ -1,3 +1,3 @@ -pub const Reader = @import("squashfs.zig").Reader; +pub const Reader = @import("reader.zig").Reader; pub const File = @import("file.zig").File; diff --git a/src/squashfs.zig b/src/squashfs.zig deleted file mode 100644 index c04adc5..0000000 --- a/src/squashfs.zig +++ /dev/null @@ -1,54 +0,0 @@ -const std = @import("std"); -const fs = std.fs; - -const Superblock = @import("superblock.zig").Superblock; -const inode = @import("inode.zig"); -const MetadataReader = @import("metadata_reader.zig").MetadataReader; -const File = @import("file.zig").File; -const FileOffsetReader = @import("file_offset_reader.zig").FileOffsetReader; - -pub const Reader = struct { - super: Superblock, - rdr: fs.File, - root: File, - alloc: std.heap.ArenaAllocator, - - pub fn init(filename: []const u8) !Reader { - var file = try std.fs.cwd().openFile(filename, .{}); - errdefer file.close(); - var alloc: std.heap.ArenaAllocator = .init(std.heap.smp_allocator); - errdefer _ = alloc.deinit(); - const super = try file.reader().readStruct(Superblock); - try super.valid(); - var offset_rdr: FileOffsetReader = .init(&file, super.inode_table + super.root_inode.block_start); - var root_reader: MetadataReader = try .init( - super.comp, - offset_rdr.any(), - alloc.allocator(), - ); - defer root_reader.deinit(); - try root_reader.skip(super.root_inode.offset); - var out: Reader = .{ - .super = super, - .rdr = file, - .root = undefined, - .alloc = alloc, - }; - out.root = .{ - .inode = try inode.readInode(root_reader.any(), super.block_size, alloc.allocator()), - .name = "", - .rdr = &out, - }; - std.debug.print("init {}\n", .{out}); - return out; - } - - pub fn deinit(self: *Reader) void { - self.rdr.close(); - self.alloc.deinit(); - } - - pub fn open(self: *Reader, path: []const u8) !File { - return self.root.open(path); - } -}; diff --git a/src/superblock.zig b/src/superblock.zig index 1ae96f7..b1d255a 100644 --- a/src/superblock.zig +++ b/src/superblock.zig @@ -1,52 +1,37 @@ const math = @import("std").math; -const InodeRef = @import("inode.zig").InodeRef; -const CompressionType = @import("decompress.zig").CompressionType; -pub const SuperblockError = error{ +const SuperblockError = error{ InvalidMagic, - InvalidLog, + InvalidBlockLog, InvalidVersion, }; pub const Superblock = packed struct { magic: u32, - count: u32, + inode_count: u32, mod_time: u32, block_size: u32, - frags: u32, - comp: CompressionType, + frag_count: u32, + decomp: @import("decompress.zig").DecompressType, block_log: u16, - flags: packed struct { - inode_uncomp: bool, - data_uncomp: bool, - _unused: bool, - frag_uncomp: bool, - frag_always: bool, - data_dedupe: bool, - export_table: bool, - xattr_uncomp: bool, - no_xattr: bool, - comp_options: bool, - id_uncomp: bool, - _padding: u5, - }, + flags: u16, id_count: u16, ver_maj: u16, ver_min: u16, - root_inode: InodeRef, + root_ref: @import("inode/inode.zig").InodeRef, size: u64, - id_table: u64, - xattr_table: u64, - inode_table: u64, - dir_table: u64, - frag_table: u64, - export_table: u64, + id_table_start: u64, + xattr_table_start: u64, + inode_table_start: u64, + dir_table_start: u64, + frag_table_start: u64, + export_table_start: u64, - pub fn valid(self: Superblock) SuperblockError!void { + pub fn validate(self: Superblock) SuperblockError!void { if (self.magic != 0x73717368) { return SuperblockError.InvalidMagic; } else if (self.block_log != math.log2(self.block_size)) { - return SuperblockError.InvalidLog; + return SuperblockError.InvalidBlockLog; } else if (self.ver_maj != 4 or self.ver_min != 0) { return SuperblockError.InvalidVersion; } diff --git a/src/test_squashfs.zig b/src/test_squashfs.zig deleted file mode 100644 index 510f897..0000000 --- a/src/test_squashfs.zig +++ /dev/null @@ -1,13 +0,0 @@ -const std = @import("std"); -const debug = std.debug; -const squashfs = @import("squashfs.zig"); -const print = std.debug.print; - -const testFileName = "testing/LinuxPATest.sfs"; - -test "open test file" { - var reader = try squashfs.newReader(testFileName); - defer reader.deinit(); - const fil = try reader.open("PortableApps/Desktop.ini"); - std.debug.print("{s}\n", .{fil.name}); -}