diff --git a/src/file.zig b/src/file.zig index 10b6b92..1b90847 100644 --- a/src/file.zig +++ b/src/file.zig @@ -1,6 +1,7 @@ const std = @import("std"); const io = std.io; const fs = std.fs; +const builtin = @import("builtin"); const inode = @import("inode/inode.zig"); const directory = @import("directory.zig"); @@ -36,16 +37,17 @@ pub const File = struct { ); defer meta_rdr.deinit(); try meta_rdr.skip(ent.offset); - const out: File = .{ - .name = try rdr.alloc.alloc(u8, ent.name.len), + const name = try rdr.alloc.alloc(u8, ent.name.len); + errdefer rdr.alloc.free(name); + @memcpy(name, ent.name); + return .{ + .name = name, .inode = try .init( rdr.alloc, meta_rdr.any(), rdr.super.block_size, ), }; - @memcpy(out.name, ent.name); - return out; } pub fn deinit(self: *File, alloc: std.mem.Allocator) void { @@ -159,7 +161,7 @@ pub const File = struct { return self.data_rdr.?.read(bytes); } - pub const FileReader = io.GenericReader(*File, (FileError || anyerror), read); + const FileReader = io.GenericReader(*File, (FileError || anyerror), read); pub fn reader(self: *File) FileReader { return .{ @@ -199,30 +201,54 @@ pub const File = struct { } var iter = try self.iterator(rdr); defer iter.deinit(); - while (iter.next()) |f| { - //TODO: Threading - try f.extractReal(rdr, path + "/" + f.name, false); + while (iter.next()) |*f| { + const extr_path = try std.mem.concat(rdr.alloc, u8, &[3][]const u8{ real_path, "/", f.name }); + defer rdr.alloc.free(extr_path); + try @constCast(f).extractReal(rdr, extr_path, false); } }, .file, .ext_file => { - if (exists) { - if (!first) { - return ExtractError.FileExists; - } else { - if (stat.?.kind == .directory) { - real_path += "/" + self.name; - } else { - return ExtractError.FileExists; - } - } - } - const ext = try self.extractor(rdr); + if ((!first and exists) or + (first and exists and stat.?.kind != .directory)) return ExtractError.FileExists; + const extr_path = if (first and exists and stat.?.kind == .directory) blk: { + break :blk try std.mem.concat(rdr.alloc, u8, &[3][]const u8{ real_path, "/", self.name }); + } else blk: { + const tmp = try rdr.alloc.alloc(u8, real_path.len); + @memcpy(tmp, real_path); + break :blk tmp; + }; + defer rdr.alloc.free(extr_path); + var ext = try self.extractor(rdr); defer ext.deinit(); - const fil = try fs.cwd().createFile(real_path, .{}); - try ext.writeToFile(fil); + const fil = try fs.cwd().createFile(extr_path, .{}); + defer fil.close(); + try ext.writeToFile(try .init(), &fil); }, - .sym, .ext_sym => {}, - .block, .ext_block, .char, .ext_char => {}, + .sym, .ext_sym => { + if (exists) return ExtractError.FileExists; + try fs.cwd().symLink(try self.symPath(), real_path, .{}); + }, + .block, .ext_block, .char, .ext_char, .fifo, .ext_fifo => { + if (exists) return ExtractError.FileExists; + comptime if (builtin.os.tag != .linux) return; + const IFCHR: u32 = 0o020000; + const IFBLK: u32 = 0o060000; + const IFIFO: u32 = 0o010000; + const mode = switch (self.inode.header.inode_type) { + .block, .ext_block => IFBLK, + .char, .ext_char => IFCHR, + .fifo, .ext_fifo => IFIFO, + else => unreachable, + }; + const dev = switch (self.inode.data) { + .block, .char => |b| b.device, + .ext_block, .ext_char => |b| b.device, + .fifo, .ext_fifo => 0, + else => unreachable, + }; + _ = std.os.linux.mknod(@ptrCast(real_path), mode, dev); + }, + .sock, .ext_sock => {}, } //TODO: permissions } diff --git a/src/inode/file.zig b/src/inode/file.zig index 4f70b19..e726394 100644 --- a/src/inode/file.zig +++ b/src/inode/file.zig @@ -15,7 +15,7 @@ pub const FileInode = struct { blocks: []const BlockSize, pub fn init(alloc: std.mem.Allocator, rdr: io.AnyReader, block_size: u32) !FileInode { - const fixed_buf = [16]u8{}; + var fixed_buf = [1]u8{0} ** 16; _ = try rdr.readAll(&fixed_buf); const frag_idx = std.mem.bytesToValue(u32, fixed_buf[4..8]); const size = std.mem.bytesToValue(u32, fixed_buf[12..16]); @@ -49,7 +49,7 @@ pub const ExtFileInode = struct { blocks: []const BlockSize, pub fn init(alloc: std.mem.Allocator, rdr: io.AnyReader, block_size: u32) !ExtFileInode { - var fixed_buf = [40]u8{}; + var fixed_buf = [1]u8{0} ** 40; _ = try rdr.readAll(&fixed_buf); const size = std.mem.bytesToValue(u64, fixed_buf[8..16]); const frag_idx = std.mem.bytesToValue(u32, fixed_buf[28..32]); diff --git a/src/reader.zig b/src/reader.zig index d241d43..694d0a2 100644 --- a/src/reader.zig +++ b/src/reader.zig @@ -80,8 +80,6 @@ pub const Reader = struct { test "root iter" { 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(); var rootIter = try rdr.root.iterator(&rdr); @@ -90,3 +88,12 @@ test "root iter" { std.debug.print("{s}\n", .{f.name}); } } + +test "extract" { + const test_sfs_path = "testing/LinuxPATest.sfs"; + const extract_path = "testing/testExtract"; + try std.fs.cwd().deleteTree(extract_path); + var rdr: Reader = try .init(std.testing.allocator, test_sfs_path, 0); + defer rdr.deinit(); + try rdr.root.extract(&rdr, extract_path); +} diff --git a/src/readers/data_extractor.zig b/src/readers/data_extractor.zig index 437e729..eae6065 100644 --- a/src/readers/data_extractor.zig +++ b/src/readers/data_extractor.zig @@ -28,7 +28,7 @@ pub const DataExtractor = struct { max_mem: u64, pub fn init() !Config { return .{ - .thread_count = try std.Thread.getCpuCount(), + .thread_count = @truncate(try std.Thread.getCpuCount()), .max_mem = comptime 1024 * 1024 * 1024, }; } @@ -42,17 +42,17 @@ pub const DataExtractor = struct { var frag_offset: u32 = 0; switch (fil.inode.data) { .file => |f| { + data_start = f.data_start; sizes = try reader.alloc.alloc(BlockSize, f.blocks.len); @memcpy(sizes, f.blocks); - data_start = f.data_start; size = f.size; frag_idx = f.frag_idx; frag_offset = f.frag_offset; }, .ext_file => |f| { + data_start = f.data_start; sizes = try reader.alloc.alloc(BlockSize, f.blocks.len); @memcpy(sizes, f.blocks); - data_start = f.data_start; size = f.size; frag_idx = f.frag_idx; frag_offset = f.frag_offset; @@ -62,25 +62,25 @@ pub const DataExtractor = struct { var out: DataExtractor = .{ .alloc = reader.alloc, .decomp = reader.super.decomp, - .holder = reader.holder, + .holder = &reader.holder, .block_size = reader.super.block_size, .sizes = sizes, .block_offset = try reader.alloc.alloc(u64, sizes.len), - .data_start = data_start, }; errdefer out.deinit(); var offset: u64 = data_start; - for (0..out.block_offset) |i| { + for (0.., out.block_offset) |i, _| { out.block_offset[i] = offset; offset += out.sizes[i].size; } if (frag_idx != 0xFFFFFFFF) { - const frag_entry = try reader.frag_table.getValue(frag_idx); + const frag_entry = try reader.frag_table.getValue(reader, frag_idx); var frag_rdr: DataReader = try .fromFragEntry(reader, frag_entry); + std.debug.print("{} {}\n", .{ frag_offset, 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); + _ = try frag_rdr.any().readAll(out.frag_data.?); } return out; } @@ -88,8 +88,7 @@ pub const DataExtractor = struct { pub fn deinit(self: *DataExtractor) void { self.alloc.free(self.sizes); self.alloc.free(self.block_offset); - if (self.cur_bloc.len > 0) self.alloc.free(self.cur_bloc); - if (self.frag_data != null) self.alloc.free(self.frag_data); + if (self.frag_data != null) self.alloc.free(self.frag_data.?); } fn processBlock(self: DataExtractor, block_ind: u32) ![]u8 { @@ -98,7 +97,7 @@ pub const DataExtractor = struct { //TODO } - fn processBlockToFile(self: DataExtractor, block_ind: u32, fil: *fs.File) !void { + fn processBlockToFile(self: DataExtractor, block_ind: u32, fil: *const fs.File) !void { _ = self; _ = block_ind; _ = fil; @@ -110,7 +109,7 @@ pub const DataExtractor = struct { /// Returns the amount of bytes written. /// /// Optimized for lower memory usage by using File.pwrite. - pub fn writeToFile(self: DataExtractor, conf: Config, fil: *fs.File) !void { + pub fn writeToFile(self: DataExtractor, conf: Config, fil: *const fs.File) !void { _ = self; _ = fil; _ = conf; diff --git a/src/readers/data_reader.zig b/src/readers/data_reader.zig index df5d764..200730e 100644 --- a/src/readers/data_reader.zig +++ b/src/readers/data_reader.zig @@ -23,7 +23,7 @@ pub const DataReader = struct { frag_data: ?[]u8 = null, next_block_num: u32 = 0, - cur_bloc: []u8 = undefined, + cur_bloc: []u8 = &[0]u8{}, cur_offset: u32 = 0, pub fn init(fil: *File, reader: *Reader) !DataReader { @@ -84,17 +84,17 @@ 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_data != null) self.alloc.free(self.frag_data); + if (self.frag_data != null) self.alloc.free(self.frag_data.?); } pub fn skip(self: *DataReader, offset: u32) !void { var cur_skip: u32 = 0; var to_skip: u32 = 0; while (cur_skip < offset) { - if (self.offset >= self.block.len) try self.readNextBlock(); - to_skip = @min(offset - cur_skip, self.block.len - self.offset); + if (self.cur_offset >= self.cur_bloc.len) try self.readNextBlock(); + to_skip = @min(offset - cur_skip, self.cur_bloc.len - self.cur_offset); cur_skip += to_skip; - self.offset += to_skip; + self.cur_offset += to_skip; } } @@ -106,8 +106,8 @@ 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_data != null) { - try self.sizeBlock(self.frag_data.len); - @memcpy(self.cur_bloc, self.frag_data); + try self.sizeBlock(self.frag_data.?.len); + @memcpy(self.cur_bloc, self.frag_data.?); return; } if (siz.size == 0) { @@ -120,14 +120,14 @@ pub const DataReader = struct { _ = try self.rdr.any().readAll(self.cur_bloc); } else { self.alloc.free(self.cur_bloc); - var limit = std.io.limitedReader(self.reader, siz.size); + var limit = std.io.limitedReader(self.rdr, siz.size); var dat = try self.decomp.decompress(self.alloc, limit.reader().any()); - self.block = try dat.toOwnedSlice(); + self.cur_bloc = try dat.toOwnedSlice(); } } - fn sizeBlock(self: *DataReader, size: u32) !void { - if (!self.alloc.resize(u8, size)) { + fn sizeBlock(self: *DataReader, size: usize) !void { + if (!self.alloc.resize(self.cur_bloc, size)) { self.alloc.free(self.cur_bloc); self.cur_bloc = try self.alloc.alloc(u8, size); } @@ -137,15 +137,15 @@ pub const DataReader = struct { var cur_read: usize = 0; var to_read: usize = 0; while (cur_read < bytes.len) { - if (self.offset >= self.block.len) { - if (self.readNextBlock()) |err| { + if (self.cur_offset >= self.cur_bloc.len) { + self.readNextBlock() catch |err| { if (err == DataReaderError.EOF) return cur_read; return err; - } + }; } - to_read = @min(bytes.len - cur_read, self.block.len - self.offset); - @memcpy(bytes[cur_read..], self.block[self.offset .. @as(usize, self.offset) + to_read]); - self.offset += @truncate(to_read); + to_read = @min(bytes.len - cur_read, self.cur_bloc.len - self.cur_offset); + @memcpy(bytes[cur_read..], self.cur_bloc[self.cur_offset .. @as(usize, self.cur_offset) + to_read]); + self.cur_offset += @truncate(to_read); cur_read += to_read; } return cur_read; diff --git a/src/readers/file_holder.zig b/src/readers/file_holder.zig index befc2cb..c211223 100644 --- a/src/readers/file_holder.zig +++ b/src/readers/file_holder.zig @@ -33,6 +33,8 @@ pub const FileOffsetReader = struct { file: *File, offset: u64, + pub const Error = fs.File.PReadError; + pub fn read(self: *FileOffsetReader, bytes: []u8) !usize { const red = try self.file.preadAll(bytes, self.offset); self.offset += red; diff --git a/src/table.zig b/src/table.zig index 5fbc3bd..9987cd3 100644 --- a/src/table.zig +++ b/src/table.zig @@ -13,7 +13,6 @@ pub fn Table( comptime T: type, ) type { return struct { - alloc: std.mem.Allocator, decomp: DecompressType, table: []T = &[0]T{}, offset: u64, @@ -21,14 +20,13 @@ pub fn Table( pub fn init(read: *Reader, offset: u64, item_count: u32) Self { return .{ - .alloc = read.alloc, .decomp = read.super.decomp, .offset = offset, .item_count = item_count, }; } pub fn deinit(self: *Self, alloc: std.mem.Allocator) void { - alloc.free(self.table); + if (self.table.len == 0) alloc.free(self.table); } pub fn getValue(self: *Self, read: *Reader, i: u64) !T { @@ -36,23 +34,21 @@ pub fn Table( if (self.table.len > i) return self.table[i]; var meta_rdr: MetadataReader = undefined; var offset_rdr: FileOffsetReader = undefined; + var meta_buf: [8]u8 = [1]u8{0} ** 8; var meta_offset: u64 = 0; var to_read: u32 = 0; while (self.table.len <= i) { - _ = try read.holder.file.preadAll(std.mem.sliceAsBytes(&meta_offset), self.offset); + _ = try read.holder.file.preadAll(&meta_buf, self.offset); self.offset += 8; + meta_offset = std.mem.bytesToValue(u8, &meta_buf); offset_rdr = read.holder.readerAt(meta_offset); - meta_rdr = .init(self.alloc, self.decomp, offset_rdr.any()); + meta_rdr = .init(read.alloc, self.decomp, offset_rdr.any()); defer meta_rdr.deinit(); - to_read = @min(self.item_count - self.table.len, comptime blk: { - break :blk 8192 / @sizeOf(T); - }); - if (!self.alloc.resize(self.table, self.table.len + to_read)) { - const alloc_size = self.table.len + to_read; - self.alloc.free(self.table); - self.table = try self.alloc.alloc(T, alloc_size); - } - _ = try meta_rdr.any().readAll(std.mem.asBytes(self.table[self.table.len - to_read ..])); + to_read = @min(self.item_count - self.table.len, comptime 8192 / @sizeOf(T)); + const alloc_size = self.table.len + to_read; + if (self.table.len != 0) read.alloc.free(self.table); + self.table = try read.alloc.alloc(T, alloc_size); + _ = try meta_rdr.any().readAll(@ptrCast(self.table[self.table.len - to_read ..])); } return self.table[i]; }