From 9f345e5fdb6bfca96489744871d80c01d35edeb2 Mon Sep 17 00:00:00 2001 From: Caleb Gardner Date: Sat, 17 May 2025 14:33:16 -0500 Subject: [PATCH] Almost there? --- src/decompress.zig | 1 + src/directory.zig | 63 ++++++++++++++++++++++++++++-- src/file.zig | 95 +++++++++++++++++++++++++++++++++++++++++---- src/inode/inode.zig | 2 +- src/reader.zig | 23 ++++++----- 5 files changed, 163 insertions(+), 21 deletions(-) diff --git a/src/decompress.zig b/src/decompress.zig index 8937c54..9029792 100644 --- a/src/decompress.zig +++ b/src/decompress.zig @@ -17,6 +17,7 @@ pub const DecompressType = enum(u16) { pub fn decompress(self: DecompressType, alloc: std.mem.Allocator, rdr: io.AnyReader) !std.ArrayList(u8) { var out = std.ArrayList(u8).init(alloc); + errdefer out.deinit(); switch (self) { .zlib => try compress.zlib.decompress(rdr, out.writer()), .lzma => { diff --git a/src/directory.zig b/src/directory.zig index 285f577..2582ff8 100644 --- a/src/directory.zig +++ b/src/directory.zig @@ -1,5 +1,62 @@ -pub const DirHeader = packed struct {}; +const std = @import("std"); +const io = std.io; -pub const RawDirEntry = struct {}; +const InodeType = @import("inode/inode.zig").InodeType; -pub const DirEntry = struct {}; +const DirHeader = packed struct { + count: u32, + inode_block_start: u32, + inode_num: u32, +}; + +const RawDirEntryStart = packed struct { + inode_block_offset: u16, + /// Difference from the current DirHeader inode_num + inode_num_difference: i16, + /// Extended inodes will be their basic type. + inode_type: InodeType, + name_size: u16, +}; + +pub const DirEntry = struct { + block_start: u32, + offset: u16, + inode_num: u32, + name: []const u8, + + fn init(alloc: std.mem.Allocator, hdr: DirHeader, rdr: io.AnyReader) !DirEntry { + const raw = try rdr.readStruct(RawDirEntryStart); + const name = try alloc.alloc(u8, raw.name_size + 1); + _ = try rdr.read(name); + return .{ + .block_start = hdr.inode_block_start, + .offset = raw.inode_block_offset, + .inode_num = if (raw.inode_num_difference > 0) + hdr.inode_num + @abs(raw.inode_num_difference) + else + hdr.inode_num - @abs(raw.inode_num_difference), + .name = name, + }; + } + + pub fn deinit(self: DirEntry, alloc: std.mem.Allocator) void { + alloc.free(self.name); + } +}; + +pub fn readDirectory(alloc: std.mem.Allocator, rdr: io.AnyReader, size: u64) !std.StringHashMap(DirEntry) { + var out: std.StringHashMap(DirEntry) = .init(alloc); + errdefer out.deinit(); + var red_size: u64 = 3; + var hdr: DirHeader = try rdr.readStruct(DirHeader); + while (red_size < size) : (hdr = try rdr.readStruct(DirHeader)) { + var i: u32 = 0; + while (i <= hdr.count) : (i += 1) { + var tmp: DirEntry = try .init(alloc, hdr, rdr); + errdefer tmp.deinit(alloc); + try out.put(tmp.name, tmp); + red_size += 8 + tmp.name.len; + } + } + return out; +} diff --git a/src/file.zig b/src/file.zig index 0b70084..93c4fbd 100644 --- a/src/file.zig +++ b/src/file.zig @@ -2,6 +2,7 @@ const std = @import("std"); const io = std.io; const inode = @import("inode/inode.zig"); +const directory = @import("directory.zig"); const Reader = @import("reader.zig").Reader; const DirEntry = @import("directory.zig").DirEntry; @@ -11,24 +12,55 @@ const FileError = error{ NotDirectory, NotNormalFile, NotSymlink, + NotFound, }; pub const File = struct { name: []const u8, inode: inode.Inode, + dirEntries: std.StringHashMap(DirEntry) = undefined, + hasEntries: bool = false, - // pub fn fromDirEntry(read: Reader, ent: DirEntry) !File {} - - pub fn open(self: File, reader: *Reader, path: []const u8) !File { - if (path.len == 0 || std.mem.eql(u8, path, ".")) { - return self; + pub fn deinit(self: *File, alloc: std.mem.Allocator) void { + self.inode.deinit(); + alloc.free(self.name); + if (self.hasEntries) { + var iter = self.dirEntries.iterator(); + while (iter.next()) |ent| { + ent.value_ptr.deinit(alloc); + } + self.dirEntries.deinit(); } - switch (inode.InodeHeader.inode_type) { + } + + pub fn open(self: *File, reader: *Reader, path: []const u8) !File { + return self.realOpen(reader, path, true); + } + + fn realOpen(self: *File, reader: *Reader, path: []const u8, first: bool) !File { + const clean_path: []const u8 = std.mem.trimLeft(u8, path, "/"); + if (clean_path.len == 0) { + return self.*; + } + if (!first) { + defer reader.alloc.free(path); + defer self.deinit(reader.alloc); + } + switch (self.inode.header.inode_type) { .dir, .ext_dir => {}, else => return FileError.NotDirectory, } - _ = reader; - //TODO: read dir entries and find correct inode and file + if (!self.hasEntries) { + try self.readDirEntries(reader); + } + const split_idx = std.mem.indexOf(u8, clean_path, "/") orelse clean_path.len; + const name = clean_path[0..split_idx]; + const ent = self.dirEntries.get(name); + if (ent == null) { + return FileError.NotFound; + } + var fil = try fileFromDirEntry(reader, ent.?); + return fil.realOpen(reader, clean_path[split_idx..], false); } pub fn symPath(self: File) ![]const u8 { @@ -38,4 +70,51 @@ pub const File = struct { else => FileError.NotSymlink, }; } + + fn readDirEntries(self: *File, reader: *Reader) !void { + var block_start: u32 = 0; + var offset: u16 = 0; + var size: u32 = 0; + switch (self.inode.data) { + .dir => |d| { + block_start = d.block_start; + offset = d.offset; + size = d.size; + }, + .ext_dir => |d| { + block_start = d.block_start; + offset = d.offset; + size = d.size; + }, + else => return FileError.NotDirectory, + } + var offset_rdr = reader.holder.readerAt(reader.super.dir_table_start + block_start); + var meta_rdr: MetadataReader = try .init( + reader.alloc, + offset_rdr.any(), + reader.super.decomp, + ); + defer meta_rdr.deinit(); + self.dirEntries = try directory.readDirectory(reader.alloc, meta_rdr.any(), size); + self.hasEntries = true; + } }; + +fn fileFromDirEntry(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, + offset_rdr.any(), + read.super.decomp, + ); + defer meta_rdr.deinit(); + try meta_rdr.skip(ent.offset); + return .{ + .name = ent.name, + .inode = try .init( + read.alloc, + meta_rdr.any(), + read.super.block_size, + ), + }; +} diff --git a/src/inode/inode.zig b/src/inode/inode.zig index 8bf82d8..a67a0c6 100644 --- a/src/inode/inode.zig +++ b/src/inode/inode.zig @@ -15,7 +15,7 @@ pub const InodeRef = packed struct { }; pub const InodeType = enum(u16) { - dir, + dir = 1, file, sym, block, diff --git a/src/reader.zig b/src/reader.zig index 8f56876..d421394 100644 --- a/src/reader.zig +++ b/src/reader.zig @@ -6,9 +6,10 @@ const FileHolder = @import("readers/file_holder.zig").FileHolder; const Superblock = @import("superblock.zig").Superblock; const File = @import("file.zig").File; const MetadataReader = @import("readers/metadata.zig").MetadataReader; +const DirEntry = @import("directory.zig").DirEntry; pub const Reader = struct { - arena: std.heap.ArenaAllocator, + alloc: std.mem.Allocator, holder: FileHolder, super: Superblock, root: File, @@ -17,9 +18,8 @@ pub const Reader = struct { var holder: FileHolder = try .init(filepath, offset); const super: Superblock = try holder.reader().readStruct(Superblock); try super.validate(); - const arena: std.heap.ArenaAllocator = .init(alloc); var out: Reader = .{ - .arena = arena, + .alloc = alloc, .holder = holder, .super = super, .root = undefined, @@ -28,14 +28,14 @@ pub const Reader = struct { return out; } pub fn deinit(self: *Reader) void { - self.arena.deinit(); + self.root.deinit(self.alloc); self.holder.deinit(); } fn fileFromRef(self: *Reader, ref: inode.InodeRef, name: []const u8) !File { var offset_rdr = self.holder.readerAt(ref.block_start + self.super.inode_table_start); var meta_rdr: MetadataReader = try .init( - self.arena.allocator(), + self.alloc, offset_rdr.any(), self.super.decomp, ); @@ -44,7 +44,7 @@ pub const Reader = struct { return .{ .name = name, .inode = try .init( - self.arena.allocator(), + self.alloc, meta_rdr.any(), self.super.block_size, ), @@ -53,8 +53,13 @@ pub const Reader = struct { }; test "reader" { - const test_file_path = "testing/LinuxPATest.sfs"; - var rdr: Reader = try .init(std.testing.allocator, test_file_path, 0); + const test_sfs_path = "testing/LinuxPATest.sfs"; + 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(); - std.debug.print("{}\n", .{rdr.root}); + var fil = try rdr.root.open(&rdr, test_file_path); + defer fil.deinit(rdr.alloc); + + std.debug.print("{}\n", .{fil}); }