From eec468ff171d64be6aba1bd0b768cd5382588625 Mon Sep 17 00:00:00 2001 From: "Caleb J. Gardner" Date: Sat, 4 Apr 2026 18:48:57 -0500 Subject: [PATCH] File stuff --- src/archive.zig | 42 +++++++++++++++++++++++++++++--------- src/file.zig | 47 +++++++++++++++++++++++++++++++++++++++++-- src/util/metadata.zig | 9 +++++---- src/util/utils.zig | 24 ++++++++++++++++++++++ 4 files changed, 106 insertions(+), 16 deletions(-) create mode 100644 src/util/utils.zig diff --git a/src/archive.zig b/src/archive.zig index eb6befc..7287768 100644 --- a/src/archive.zig +++ b/src/archive.zig @@ -9,6 +9,7 @@ const BlockSize = @import("inode/file.zig").BlockSize; const LookupTable = @import("lookup_table.zig"); const MetadataReader = @import("util/metadata.zig"); const OffsetFile = @import("util/offset_file.zig"); +const Utils = @import("util/utils.zig"); pub const Error = error{ BadMagic, @@ -53,11 +54,23 @@ pub fn root(self: Archive, alloc: std.mem.Allocator) !File { return .{ .alloc = alloc, - .inode = try self.refToInode(alloc, self.super.root_ref), + .inode = try Utils.refToInode( + alloc, + &self.stateless_decomp, + self.file, + self.super.inode_start, + self.super.block_size, + self.super.root_ref, + ), .name = "", }; } -pub fn open(self: Archive, alloc: std.mem.Allocator, path: []const u8) !File {} +pub fn open(self: Archive, alloc: std.mem.Allocator, path: []const u8) !File { + if (Utils.pathIsSelf(path)) return self.root(alloc); + var root_file = self.root(alloc); + defer root_file.deinit(); + return root_file.open(alloc, path); +} pub fn fragEntry(self: Archive, idx: u32) !FragEntry { return LookupTable.stateless(FragEntry, self.fil, &self.stateless_decomp, self.super.frag_start, idx); @@ -67,14 +80,7 @@ pub fn id(self: Archive, idx: u32) !u16 { } pub fn inode(self: Archive, alloc: std.mem.Allocator, inode_num: u32) !Inode { const ref = try LookupTable.stateless(Inode.Ref, self.file, &self.stateless_decomp, self.super.export_start, inode_num - 1); - return self.refToInode(alloc, ref); -} - -inline fn refToInode(self: Archive, alloc: std.mem.Allocator, ref: Inode.Ref) !Inode { - var rdr = self.file.readerAt(self.super.inode_start + ref.block_start, &[0]u8{}); - var meta: MetadataReader = .init(&rdr.interface, &self.stateless_decomp); - try meta.interface.discardAll(ref.block_offset); - return .read(alloc, &meta.interface, self.super.block_size); + return Utils.refToInode(alloc, &self.stateless_decomp, self.file, self.super.inode_start, self.super.block_size, ref); } // Superblock @@ -127,9 +133,25 @@ pub const Superblock = packed struct { if (std.math.log2(self.block_size) != self.block_log) return Error.BadBlockLog; } + + pub fn toMinimal(self: Superblock) MinimalSuperblock { + return .{ + .inode_count = self.inode_count, + .block_size = self.block_size, + .frag_count = self.frag_count, + .id_count = self.id_count, + .id_start = self.id_start, + .xattr_start = self.xattr_start, + .inode_start = self.inode_start, + .dir_start = self.dir_start, + .frag_start = self.frag_start, + .export_start = self.export_start, + }; + } }; pub const MinimalSuperblock = struct { + inode_count: u32, block_size: u32, frag_count: u32, id_count: u16, diff --git a/src/file.zig b/src/file.zig index 6979a87..b37bd45 100644 --- a/src/file.zig +++ b/src/file.zig @@ -1,15 +1,23 @@ const std = @import("std"); const Archive = @import("archive.zig"); +const Decompressor = @import("decomp.zig"); const Directory = @import("directory.zig"); const Inode = @import("inode.zig"); const MetadataReader = @import("util/metadata.zig"); +const Utils = @import("util/utils.zig"); + +pub const Error = error{ + NotDirectory, + NotRegularFile, +}; const File = @This(); alloc: std.mem.Allocator, -archive: Archive, +superblock: Archive.MinimalSuperblock, +decomp: Decompressor, name: []const u8, inode: Inode, @@ -19,8 +27,43 @@ pub fn init(alloc: std.mem.Allocator, archive: Archive, entry: Directory.Entry) errdefer alloc.free(new_name); @memcpy(new_name, entry.name); var rdr = archive.file.readerAt(archive.super.inode_start + entry.block_start, &[0]u8{}); - var meta + var meta: MetadataReader = .init(&rdr.interface, &archive.stateless_decomp); + try meta.interface.discardAll(entry.block_offset); + return .{ + .alloc = alloc, + .superblock = archive.super, + .decomp = .{ + .alloc = alloc, + .vtable = &.{ .stateless = archive.stateless_decomp.vtable.stateless }, + }, + .name = new_name, + .inode = try .read(alloc, &meta.interface, archive.super.block_size), + }; } pub fn deinit(self: File) void { self.alloc.free(self.name); } + +/// Opens a sub-directory. If the given path is "", ".", "/", or "./", a copy of the File is returned. +pub fn open(self: File, alloc: std.mem.Allocator, path: []const u8) !File { + switch (self.inode.hdr.inode_type) { + .dir, .ext_dir => {}, + else => Error.NotDirectory, + } + if (Utils.pathIsSelf(path)) { + const new_name = try alloc.alloc(u8, self.name.len); + @memcpy(new_name, self.name); + return .{ + .alloc = alloc, + .superblock = self.superblock, + .decomp = .{ + .alloc = alloc, + .vtable = &.{ .stateless = self.decomp.vtable.stateless }, + }, + .name = new_name, + .inode = self.inode, + }; + } + + return error.TODO; +} diff --git a/src/util/metadata.zig b/src/util/metadata.zig index f6fb78e..da50adc 100644 --- a/src/util/metadata.zig +++ b/src/util/metadata.zig @@ -40,10 +40,11 @@ fn advanceBuffer(self: *MetadataReader) Reader.Error!void { self.interface.end = hdr.size; return; } - self.interface.end = self.decomp.decompress(self.read_buf[0..hdr.size], self.interface.buffer) catch |err| return switch (err) { - error.OutOfMemory => error.ReadFailed, - else => err, - }; + self.interface.end = self.decomp.decompress(self.read_buf[0..hdr.size], self.interface.buffer) catch |err| + return switch (err) { + error.OutOfMemory => error.ReadFailed, + else => err, + }; } fn stream(rdr: *Reader, wrt: *Writer, limit: Limit) Reader.StreamError!usize { diff --git a/src/util/utils.zig b/src/util/utils.zig new file mode 100644 index 0000000..2d47a37 --- /dev/null +++ b/src/util/utils.zig @@ -0,0 +1,24 @@ +const std = @import("std"); + +const Decompressor = @import("../decomp.zig"); +const Inode = @import("../inode.zig"); +const MetadataReader = @import("metadata.zig"); +const OffsetFile = @import("offset_file.zig"); + +pub fn pathIsSelf(path: []const u8) bool { + if (path.len == 0) return true; + if (path.len == 1) { + return switch (path[0]) { + '.', '/' => true, + else => false, + }; + } + return std.mem.eql(u8, path, "./"); +} + +pub fn refToInode(alloc: std.mem.Allocator, decomp: *const Decompressor, fil: OffsetFile, inode_start: u64, block_size: u32, ref: Inode.Ref) !Inode { + var rdr = try fil.readerAt(inode_start + ref.block_start, &[0]u8{}); + var meta: MetadataReader = .init(&rdr.interface, decomp); + try meta.interface.discardAll(ref.block_offset); + return .read(alloc, &meta.interface, block_size); +}