diff --git a/src/extract_options.zig b/src/extract_options.zig index 3857943..4658940 100644 --- a/src/extract_options.zig +++ b/src/extract_options.zig @@ -1,6 +1,24 @@ -dereference_symlinks: bool, -unbreak_symlinks: bool, -ignore_permissions: bool, +const std = @import("std"); + +const Self = @This(); + +/// Replace symlinks with their targets +dereference_symlinks: bool = false, +/// Always extract a symlink's target if it's part of the archive. +/// May result in the symlink's target being changed. +unbreak_symlinks: bool = false, +/// Do not set file's permissions & owner when extracted. +ignore_permissions: bool = false, -processor_count: u16, // max_memory: u64, + +pol: std.Thread.Pool = undefined, + +pub fn init(alloc: std.mem.Allocator, thread_count: u16) !Self { + var out: Self = .{}; + out.pol.init(.{ + .allocator = alloc, + .n_jobs = thread_count, + }); + return out; +} diff --git a/src/file.zig b/src/file.zig index 5f96003..1dc4990 100644 --- a/src/file.zig +++ b/src/file.zig @@ -1,3 +1,84 @@ +const std = @import("std"); + +const dir = @import("directory.zig"); + +const DirEntry = dir.Entry; +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 Compression = @import("superblock.zig").Compression; +const MetadataReader = @import("reader/metadata.zig").MetadataReader; + +pub const FileError = error{ + NotRegular, + NotDirectory, +}; + pub fn File(comptime T: type) type { - return struct {}; + return struct { + const Self = @This(); + + rdr: *SfsReader(T), + + inode: Inode, + name: []const u8, + + /// Directory entries. Only populated on directories. + entries: ?[]DirEntry = null, + /// File reader. Only populated on regular files. + // data_reader: ?DataReader + + pub fn init(rdr: *SfsReader(T), inode: Inode, name: []const u8) !Self { + var out = Self{ + .rdr = rdr, + .inode = inode, + .name = name, + }; + switch (inode.data) { + .dir => |d| { + const 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); + }, + .ext_dir => |d| { + const 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); + }, + .file => |f| { + _ = f; + //TODO + }, + .ext_file => |f| { + _ = f; + //TODO + }, + } + return out; + } + pub fn deinit(self: Self) void { + self.rdr.alloc.free(self.name); + self.inode.deinit(self.rdr.alloc); + if (self.entries != null) { + for (self.entries.?) |e| { + e.deinit(self.rdr.alloc); + } + self.rdr.alloc.free(self.entries.?); + } + // if(self.data_reader != null){ + // self.data_reader.?.deinit(); + // } + } + }; } diff --git a/src/inode.zig b/src/inode.zig index b44583d..b36ceca 100644 --- a/src/inode.zig +++ b/src/inode.zig @@ -4,6 +4,8 @@ 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; @@ -86,10 +88,11 @@ pub fn init(rdr: anytype, alloc: std.mem.Allocator, block_size: u32) !Self { .data = data, }; } -pub fn initFromRef(p_rdr: anytype, comp: Compression, ref: Ref, table_start: u64, alloc: std.mem.Allocator, block_size: u32) !Self { - const rdr: ToRead(@TypeOf(p_rdr)) = .init(p_rdr, ref.block + table_start); - const meta_rdr: MetadataReader(ToRead(@TypeOf(p_rdr))) = try .init(alloc, comp, rdr); - defer meta_rdr.deinit(); - try meta_rdr.skip(ref.offset); - return init(meta_rdr, alloc, block_size); +pub fn deinit(self: Self, alloc: std.mem.Allocator) void { + switch (self.data) { + .file => |f| alloc.free(f.block_sizes), + .ext_file => |f| alloc.free(f.block_sizes), + .symlink => |s| alloc.free(s.target), + .ext_symlink => |s| alloc.free(s.target), + } } diff --git a/src/reader.zig b/src/reader.zig index e907949..9328de8 100644 --- a/src/reader.zig +++ b/src/reader.zig @@ -5,8 +5,13 @@ const Table = @import("table.zig").Table; const PRead = @import("reader/p_read.zig").PRead; const FragEntry = @import("fragment.zig").FragEntry; const Superblock = @import("superblock.zig").Superblock; +const MetadataReader = @import("reader/metadata.zig").MetadataReader; -pub fn Reader(comptime T: type) type { +pub const SfsError = error{ + NotExportable, +}; + +pub fn SfsReader(comptime T: type) type { comptime std.debug.assert(std.meta.hasFn(T, "pread")); return struct { @@ -40,14 +45,22 @@ pub fn Reader(comptime T: type) type { self.id_table.deinit(); self.frag_table.deinit(); self.export_table.deinit(); + // if (self.root != null) self.root.?.deinit(); } /// Returns the inode with the given Inode Number. - /// Requires for the archive to have an export table. - pub fn InodeAt(self: Self, num: u32) !Inode { + /// Requires the archive to have an export table. + 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); - _ = ref; - return error{TODO}.TODO; + const meta = MetadataReader(T).init( + self.alloc, + self.super.comp, + self.rdr, + self.super.inode_start + ref.block, + ); + try meta.skip(ref.offset); + return .init(meta, self.alloc, self.super.block_size); } }; } diff --git a/src/reader/data.zig b/src/reader/data.zig new file mode 100644 index 0000000..258decd --- /dev/null +++ b/src/reader/data.zig @@ -0,0 +1,55 @@ +const std = @import("std"); + +const PRead = @import("p_read.zig").Pread; +const FragEntry = @import("../fragment.zig").FragEntry; +const BlockSize = @import("../inode/file.zig").BlockSize; +const Compression = @import("../superblock.zig").Compression; + +pub fn DataReader(comptime T: type) type { + return struct { + const Self = @This(); + + alloc: std.mem.Allocator, + + rdr: PRead(T), + comp: Compression, + offsets: []BlockSize, + + file_size: u64, + block_size: u32, + sizes: []BlockSize, + + frag: []u8 = undefined, + + pub fn init( + alloc: std.mem.Allocator, + rdr: PRead(T), + comp: Compression, + init_offset: u64, + file_size: u64, + sizes: []BlockSize, + block_size: u32, + ) !Self { + var cur_offset = init_offset; + const offsets = alloc.alloc(u64, sizes.len); + for (0..sizes.len) |i| { + offsets[i] = cur_offset; + cur_offset += sizes[i].size; + } + return .{ + .alloc = alloc, + .rdr = rdr, + .comp = comp, + .offsets = offsets, + .file_size = file_size, + .block_size = block_size, + .sizes = sizes, + }; + } + + pub fn addFragment(self: *Self, entry: FragEntry, offset: u32) void { + self.frag = self.alloc.alloc(u8, self.file_size % self.block_size); + //TODO: + } + }; +} diff --git a/src/reader/metadata.zig b/src/reader/metadata.zig index 66489fc..ee9f8a5 100644 --- a/src/reader/metadata.zig +++ b/src/reader/metadata.zig @@ -1,5 +1,6 @@ const std = @import("std"); +const PRead = @import("p_read.zig").PRead; const Compression = @import("../superblock.zig").Compression; const MetaHeader = packed struct { @@ -14,36 +15,44 @@ pub fn MetadataReader(comptime T: type) type { alloc: std.mem.Allocator, comp: Compression, - rdr: T, + rdr: PRead(T), + offset: u64, block: [8192]u8 = undefined, block_size: usize = 0, block_offset: u32 = 0, - pub fn init(alloc: std.mem.Allocator, comp: Compression, rdr: T) !Self { - var out: Self = .{ + pub fn init(alloc: std.mem.Allocator, comp: Compression, rdr: PRead(T), offset: u64) Self { + return .{ .alloc = alloc, .comp = comp, .rdr = rdr, + .offset = offset, }; - try out.readNextBlock(); - return out; } fn readNextBlock(self: *Self) !void { const hdr: MetaHeader = undefined; - _ = try self.rdr.read(std.mem.asBytes(hdr)); + _ = try self.rdr.pread(std.mem.asBytes(hdr), self.offset); + self.offset += 2; self.block_size = try self.comp.decompress( 8192, self.alloc, - std.io.limitedReader(self.rdr, hdr.size), + std.io.limitedReader(self.rdr.readerAt(self.offset), hdr.size), self.block, ); + self.offset += hdr.size; self.block_offset = 0; } pub fn skip(self: *Self, offset: u32) !void { var skipped = 0; + const hdr: MetaHeader = undefined; + while (offset - skipped >= 8192) { + _ = try self.rdr.pread(std.mem.asBytes(hdr), self.offset); + self.offset += 2 + hdr.size; + skipped += 8192; + } var to_skip = 0; while (skipped < offset) { if (self.block_offset >= self.block_size) try self.readNextBlock(); diff --git a/src/root.zig b/src/root.zig index 3fd6e81..fc291d4 100644 --- a/src/root.zig +++ b/src/root.zig @@ -1,6 +1,6 @@ const std = @import("std"); -pub const Reader = @import("reader.zig").Reader; +pub const SfsReader = @import("reader.zig").SfsReader; pub const ExtractionOptions = @import("extract_options.zig"); -pub const FileReader = Reader(std.fs.File); +pub const FileReader = SfsReader(std.fs.File); diff --git a/src/superblock.zig b/src/superblock.zig index fea3886..f20bddc 100644 --- a/src/superblock.zig +++ b/src/superblock.zig @@ -38,7 +38,7 @@ pub const Superblock = packed struct { export_start: u64, }; -const DecompressError = error{ +pub const DecompressError = error{ LzoUnavailable, Lz4Unavailable, }; diff --git a/src/table.zig b/src/table.zig index 9e10ec4..2f857d0 100644 --- a/src/table.zig +++ b/src/table.zig @@ -1,6 +1,6 @@ const std = @import("std"); -const TableError = error{ +pub const TableError = error{ InvalidIndex, };