diff --git a/src/file.zig b/src/file.zig index e0ca092..8c02b15 100644 --- a/src/file.zig +++ b/src/file.zig @@ -9,6 +9,8 @@ const Reader = @import("reader.zig").Reader; const DirEntry = @import("directory.zig").DirEntry; const MetadataReader = @import("readers/metadata.zig").MetadataReader; +/// A file or directory inside of a squashfs. +/// Make sure to call deinit(); pub const File = struct { name: []const u8, inode: inode.Inode, @@ -23,23 +25,25 @@ pub const File = struct { NotFound, }; - fn fromDirEntry(read: *Reader, ent: DirEntry) !File { - var offset_rdr = read.holder.readerAt(ent.block_start + read.super.inode_table_start); - var meta_rdr: MetadataReader = try .init( - read.alloc, + fn fromDirEntry(reader: *Reader, ent: DirEntry) !File { + var offset_rdr = reader.holder.readerAt(ent.block_start + reader.super.inode_table_start); + var meta_rdr: MetadataReader = .init( + reader.alloc, + reader.super.decomp, offset_rdr.any(), - read.super.decomp, ); defer meta_rdr.deinit(); try meta_rdr.skip(ent.offset); - return .{ - .name = ent.name, + const out: File = .{ + .name = try reader.alloc.alloc(u8, ent.name.len), .inode = try .init( - read.alloc, + reader.alloc, meta_rdr.any(), - read.super.block_size, + reader.super.block_size, ), }; + std.mem.copyForwards(u8, @constCast(out.name), ent.name); + return out; } pub fn deinit(self: *File, alloc: std.mem.Allocator) void { @@ -54,6 +58,14 @@ pub const File = struct { } } + pub fn isDir(self: File) bool { + return switch (self.inode.header.inode_type) { + .dir, .ext_dir => true, + else => false, + }; + } + + /// If the File is a directory, tries to return the file at path. pub fn open(self: *File, reader: *Reader, path: []const u8) !File { return self.realOpen(reader, path, true); } @@ -79,6 +91,7 @@ pub const File = struct { return fil.realOpen(reader, clean_path[split_idx..], false); } + /// If the File is a symlink, returns the symlink's target path. pub fn symPath(self: File) ![]const u8 { return switch (self.inode.data) { .sym => |s| s.target, @@ -87,11 +100,23 @@ pub const File = struct { }; } - pub fn iterator(self: *File, read: *Reader) !FileIterator { - switch (self.inode.header.inode_type){ - .dir, ext_dir => {} + /// If the File is a directory, returns an iterator that iterates over it's children. + pub fn iterator(self: *File, reader: *Reader) !FileIterator { + switch (self.inode.header.inode_type) { + .dir, .ext_dir => {}, else => return FileError.NotDirectory, } + try self.readDirEntries(reader); + var files = try reader.alloc.alloc(File, self.dirEntries.?.count()); + var dirEntryIter = self.dirEntries.?.valueIterator(); + var i: u32 = 0; + while (dirEntryIter.next()) |ent| : (i += 1) { + files[i] = try .fromDirEntry(reader, ent.*); + } + return .{ + .alloc = reader.alloc, + .files = files, + }; } fn readDirEntries(self: *File, reader: *Reader) !void { @@ -123,6 +148,7 @@ pub const File = struct { self.dirEntries = try directory.readDirectory(reader.alloc, meta_rdr.any(), size); } + /// If the file is a normal file, reads it's data. pub fn read(self: *File, bytes: []u8) !usize { if (self.data_rdr == null) { return FileError.NotNormalFile; @@ -130,3 +156,25 @@ pub const File = struct { return self.data_rdr.?.read(bytes); } }; + +const FileIterator = struct { + alloc: std.mem.Allocator, + files: []File, + + curIndex: u32 = 0, + + pub fn next(self: *FileIterator) ?File { + if (self.curIndex >= self.files.len) return null; + defer self.curIndex += 1; + return self.files[self.curIndex]; + } + pub fn reset(self: *FileIterator) void { + self.curIndex = 0; + } + pub fn deinit(self: *FileIterator) void { + for (self.files) |*f| { + f.deinit(self.alloc); + } + self.alloc.free(self.files); + } +}; diff --git a/src/reader.zig b/src/reader.zig index ddbbd4c..a62aab1 100644 --- a/src/reader.zig +++ b/src/reader.zig @@ -10,6 +10,8 @@ const MetadataReader = @import("readers/metadata.zig").MetadataReader; const DirEntry = @import("directory.zig").DirEntry; const FragEntry = @import("readers/data.zig").FragEntry; +/// A squashfs archive reader. Make sure to call deinit(). +/// For most actions, you'll want to use the Reader.root File. pub const Reader = struct { alloc: std.mem.Allocator, holder: FileHolder, @@ -76,14 +78,15 @@ pub const Reader = struct { } }; -test "reader" { +test "root iter" { const test_sfs_path = "testing/LinuxPATest.sfs"; - const test_file_path = "PortableApps/PortableApps.com/Data/PortableAppsMenu.ini"; + // const test_file_path = "PortableApps/PortableApps.com/Data/PortableAppsMenu.ini"; var rdr: Reader = try .init(std.testing.allocator, test_sfs_path, 0); defer rdr.deinit(); - var fil = try rdr.root.open(&rdr, test_file_path); - defer fil.deinit(rdr.alloc); - - std.debug.print("{s}\n", .{fil.name}); + var rootIter = try rdr.root.iterator(&rdr); + defer rootIter.deinit(); + while (rootIter.next()) |f| { + std.debug.print("{s}\n", .{f.name}); + } } diff --git a/src/readers/data.zig b/src/readers/data.zig index 787bd91..823b626 100644 --- a/src/readers/data.zig +++ b/src/readers/data.zig @@ -19,7 +19,7 @@ pub const DataReader = struct { rdr: FileOffsetReader, block_size: u32, sizes: []BlockSize, - frag_rdr: ?DataReader = null, + frag_data: ?[]u8 = null, next_block_num: u32 = 0, cur_bloc: []u8 = undefined, @@ -60,8 +60,11 @@ pub const DataReader = struct { errdefer out.deinit(); if (frag_idx != 0xFFFFFFFF) { const frag_entry = try reader.frag_table.getValue(frag_idx); - out.frag_rdr = try .fromFragEntry(reader, frag_entry); - try out.frag_rdr.?.skip(frag_offset); + var frag_rdr = try .fromFragEntry(reader, frag_entry); + defer frag_rdr.deinit(); + try frag_rdr.skip(frag_offset); + out.frag_data = try reader.alloc.alloc(u8, size % out.block_size); + _ = try frag_rdr.any().readAll(out.frag_data); } return out; } @@ -80,7 +83,7 @@ pub const DataReader = struct { pub fn deinit(self: *DataReader) void { self.alloc.free(self.sizes); if (self.cur_bloc.len > 0) self.alloc.free(self.cur_bloc); - if (self.frag_rdr != null) self.frag_rdr.?.deinit(); + if (self.frag_data != null) self.alloc.free(self.frag_data); } pub fn skip(self: *DataReader, offset: u32) !void { @@ -101,9 +104,9 @@ pub const DataReader = struct { } const siz = self.sizes[self.next_block_num]; self.next_block_num += 1; - if (self.next_block_num == self.sizes.len - 1 and self.frag_rdr != null) { - try self.sizeBlock(self.sizes % self.block_size); - _ = try self.frag_rdr.?.readAll(self.cur_bloc); + if (self.next_block_num == self.sizes.len - 1 and self.frag_data != null) { + try self.sizeBlock(self.frag_data.len); + @memcpy(self.cur_bloc, self.frag_data); return; } if (siz.size == 0) { diff --git a/src/table.zig b/src/table.zig index 58450ed..5fbc3bd 100644 --- a/src/table.zig +++ b/src/table.zig @@ -8,6 +8,7 @@ const MetadataReader = @import("readers/metadata.zig").MetadataReader; const TableError = error{InvalidIndex}; +/// A lazily read squashfs table. pub fn Table( comptime T: type, ) type {