diff --git a/src/bin/unsquashfs.zig b/src/bin/unsquashfs.zig index 48d1f45..ceca2e0 100644 --- a/src/bin/unsquashfs.zig +++ b/src/bin/unsquashfs.zig @@ -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, diff --git a/src/directory.zig b/src/directory.zig index 36257ff..0c52b07 100644 --- a/src/directory.zig +++ b/src/directory.zig @@ -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, }); diff --git a/src/file.zig b/src/file.zig index 1dc4990..da5c0ab 100644 --- a/src/file.zig +++ b/src/file.zig @@ -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; } }; } diff --git a/src/inode.zig b/src/inode.zig index e959a4f..1acf6b1 100644 --- a/src/inode.zig +++ b/src/inode.zig @@ -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, diff --git a/src/inode/dir.zig b/src/inode/dir.zig index 8234393..dc02bf9 100644 --- a/src/inode/dir.zig +++ b/src/inode/dir.zig @@ -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; } }; diff --git a/src/inode/file.zig b/src/inode/file.zig index 5b464a5..fb6b659 100644 --- a/src/inode/file.zig +++ b/src/inode/file.zig @@ -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; + } }; diff --git a/src/inode/misc.zig b/src/inode/misc.zig index 134f9de..d9fb93a 100644 --- a/src/inode/misc.zig +++ b/src/inode/misc.zig @@ -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; } diff --git a/src/reader.zig b/src/reader.zig index 4f982c4..73b1482 100644 --- a/src/reader.zig +++ b/src/reader.zig @@ -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, diff --git a/src/reader/data.zig b/src/reader/data.zig index 2d67237..abf1f76 100644 --- a/src/reader/data.zig +++ b/src/reader/data.zig @@ -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; diff --git a/src/reader/metadata.zig b/src/reader/metadata.zig index 054a032..89d74ff 100644 --- a/src/reader/metadata.zig +++ b/src/reader/metadata.zig @@ -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; } diff --git a/src/reader/to_read.zig b/src/reader/to_read.zig index a311b4d..a72a063 100644 --- a/src/reader/to_read.zig +++ b/src/reader/to_read.zig @@ -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), + }; + } }; } diff --git a/src/root.zig b/src/root.zig index fc291d4..2869353 100644 --- a/src/root.zig +++ b/src/root.zig @@ -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(); +} diff --git a/src/superblock.zig b/src/superblock.zig index 48ad5b5..c817ca0 100644 --- a/src/superblock.zig +++ b/src/superblock.zig @@ -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); }, }