diff --git a/src/decomp/lzma.zig b/src/decomp/lzma.zig index 63ae5b2..4a720ce 100644 --- a/src/decomp/lzma.zig +++ b/src/decomp/lzma.zig @@ -19,7 +19,7 @@ alloc: std.mem.Allocator, block_size: u32, buffers: std.ArrayList(Buffer), -buffer_queue: std.SinglyLinkedList, +buffer_queue: std.SinglyLinkedList = .{}, pub fn init(alloc: std.mem.Allocator, block_size: u32) !Self { return .{ @@ -37,22 +37,31 @@ pub fn deinit(self: Self) void { fn decomp(d: ?*const Decompressor, alloc: std.mem.Allocator, in: []u8, out: []u8) Error!usize { if (d == null) { - const buf = try alloc.alloc(u8, in.len * 2); + var buf = try alloc.alloc(u8, in.len * 2); defer alloc.free(buf); - return lzmaDecomp(buf, in, out); + return lzmaDecomp(alloc, &buf, in, out) catch |err| return switch (err) { + error.OutOfMemory => Error.OutOfMemory, + else => Error.ReadFailed, + }; } - var self: Self = @fieldParentPtr("interface", d.?); + var self: *Self = @fieldParentPtr("interface", @constCast(d.?)); const buf_node = self.buffer_queue.popFirst(); var buf: *Buffer = undefined; if (buf_node == null) { const new_buf = try self.buffers.addOne(self.alloc); - new_buf.* = .{ .{}, try self.alloc.alloc(u8, self.block_size + lzma.block_size_max) }; + new_buf.* = .{ .node = .{}, .buf = try self.alloc.alloc(u8, self.block_size) }; buf = new_buf; } else { - buf = @fieldParentPtr("node", buf_node); + buf = @fieldParentPtr("node", buf_node.?); } defer self.buffer_queue.prepend(&buf.node); - return lzmaDecomp(self.alloc, &buf.buf, in, out); + return lzmaDecomp(self.alloc, &buf.buf, in, out) catch |err| { + // self.err = err; + return switch (err) { + error.OutOfMemory => Error.OutOfMemory, + else => Error.ReadFailed, + }; + }; } inline fn lzmaDecomp(alloc: std.mem.Allocator, buffer: *[]u8, in: []u8, out: []u8) !usize { diff --git a/src/decomp/xz.zig b/src/decomp/xz.zig index dfa7c75..aef9ecd 100644 --- a/src/decomp/xz.zig +++ b/src/decomp/xz.zig @@ -19,7 +19,7 @@ alloc: std.mem.Allocator, block_size: u32, buffers: std.ArrayList(Buffer), -buffer_queue: std.SinglyLinkedList, +buffer_queue: std.SinglyLinkedList = .{}, pub fn init(alloc: std.mem.Allocator, block_size: u32) !Self { return .{ @@ -37,25 +37,28 @@ pub fn deinit(self: Self) void { fn decomp(d: ?*const Decompressor, alloc: std.mem.Allocator, in: []u8, out: []u8) Error!usize { if (d == null) { - const buf = try alloc.alloc(u8, in.len * 2); + var buf = try alloc.alloc(u8, in.len * 2); defer alloc.free(buf); - return lzmaDecomp(buf, in, out); + return xzDecomp(alloc, &buf, in, out) catch return Error.ReadFailed; } - var self: Self = @fieldParentPtr("interface", d.?); + var self: *Self = @fieldParentPtr("interface", @constCast(d.?)); const buf_node = self.buffer_queue.popFirst(); var buf: *Buffer = undefined; if (buf_node == null) { const new_buf = try self.buffers.addOne(self.alloc); - new_buf.* = .{ .{}, try self.alloc.alloc(u8, self.block_size + xz.block_size_max) }; + new_buf.* = .{ .node = .{}, .buf = try self.alloc.alloc(u8, self.block_size) }; buf = new_buf; } else { - buf = @fieldParentPtr("node", buf_node); + buf = @fieldParentPtr("node", buf_node.?); } defer self.buffer_queue.prepend(&buf.node); - return lzmaDecomp(self.alloc, &buf.buf, in, out); + return xzDecomp(self.alloc, &buf.buf, in, out) catch { + // self.err = err; + return Error.ReadFailed; + }; } -inline fn lzmaDecomp(alloc: std.mem.Allocator, buffer: *[]u8, in: []u8, out: []u8) !usize { +inline fn xzDecomp(alloc: std.mem.Allocator, buffer: *[]u8, in: []u8, out: []u8) !usize { var rdr: Reader = .fixed(in); var d = try xz.Decompress.init(&rdr, alloc, buffer.*); defer { @@ -73,5 +76,5 @@ pub const stateless_decompressor: Decompressor = .{ .decomp_fn = statelessDecomp fn statelessDecomp(_: ?*const Decompressor, alloc: std.mem.Allocator, in: []u8, out: []u8) Error!usize { var buf = try alloc.alloc(u8, in.len); defer alloc.free(buf); - return lzmaDecomp(alloc, &buf, in, out) catch return Error.ReadFailed; + return xzDecomp(alloc, &buf, in, out) catch return Error.ReadFailed; } diff --git a/src/decomp/zlib.zig b/src/decomp/zlib.zig index 7907883..d155b13 100644 --- a/src/decomp/zlib.zig +++ b/src/decomp/zlib.zig @@ -19,7 +19,7 @@ alloc: std.mem.Allocator, block_size: u32, buffers: std.ArrayList(Buffer), -buffer_queue: std.SinglyLinkedList, +buffer_queue: std.SinglyLinkedList = .{}, pub fn init(alloc: std.mem.Allocator, block_size: u32) !Self { return .{ @@ -41,15 +41,15 @@ fn decomp(d: ?*const Decompressor, alloc: std.mem.Allocator, in: []u8, out: []u8 defer alloc.free(buf); return zlibDecomp(buf, in, out); } - var self: Self = @fieldParentPtr("interface", d.?); + var self: *Self = @fieldParentPtr("interface", @constCast(d.?)); const buf_node = self.buffer_queue.popFirst(); var buf: *Buffer = undefined; if (buf_node == null) { const new_buf = try self.buffers.addOne(self.alloc); - new_buf.* = .{ .{}, try self.alloc.alloc(u8, self.block_size) }; + new_buf.* = .{ .node = .{}, .buf = try self.alloc.alloc(u8, self.block_size) }; buf = new_buf; } else { - buf = @fieldParentPtr("node", buf_node); + buf = @fieldParentPtr("node", buf_node.?); } defer self.buffer_queue.prepend(&buf.node); return zlibDecomp(buf.buf, in, out); diff --git a/src/decomp/zstd.zig b/src/decomp/zstd.zig index 0042c5d..8b675c1 100644 --- a/src/decomp/zstd.zig +++ b/src/decomp/zstd.zig @@ -19,7 +19,7 @@ alloc: std.mem.Allocator, block_size: u32, buffers: std.ArrayList(Buffer), -buffer_queue: std.SinglyLinkedList, +buffer_queue: std.SinglyLinkedList = .{}, pub fn init(alloc: std.mem.Allocator, block_size: u32) !Self { return .{ @@ -41,15 +41,15 @@ fn decomp(d: ?*const Decompressor, alloc: std.mem.Allocator, in: []u8, out: []u8 defer alloc.free(buf); return zstdDecomp(buf, in, out); } - var self: Self = @fieldParentPtr("interface", d.?); + var self: *Self = @fieldParentPtr("interface", @constCast(d.?)); const buf_node = self.buffer_queue.popFirst(); var buf: *Buffer = undefined; if (buf_node == null) { const new_buf = try self.buffers.addOne(self.alloc); - new_buf.* = .{ .{}, try self.alloc.alloc(u8, self.block_size + zstd.block_size_max) }; + new_buf.* = .{ .node = .{}, .buf = try self.alloc.alloc(u8, self.block_size + zstd.block_size_max) }; buf = new_buf; } else { - buf = @fieldParentPtr("node", buf_node); + buf = @fieldParentPtr("node", buf_node.?); } defer self.buffer_queue.prepend(&buf.node); return zstdDecomp(buf.buf, in, out); diff --git a/src/directory.zig b/src/directory.zig index 9e4bbb7..d3fd314 100644 --- a/src/directory.zig +++ b/src/directory.zig @@ -31,7 +31,7 @@ pub fn readDirectory(alloc: std.mem.Allocator, rdr: *Reader, size: u32) ![]DirEn tot_red += @sizeOf(Header); - for (hdr.count + 1) |_| { + for (0..hdr.count + 1) |_| { try rdr.readSliceEndian(RawEntry, @ptrCast(&raw), .little); const new_name = try alloc.alloc(u8, raw.name_size + 1); diff --git a/src/file.zig b/src/file.zig index 06ee858..27d93ba 100644 --- a/src/file.zig +++ b/src/file.zig @@ -63,12 +63,12 @@ pub fn open(self: File, alloc: std.mem.Allocator, io: Io, filepath: []const u8) alloc.free(entries); } const path = std.mem.trim(u8, filepath, "/"); - const first_element: []u8 = std.mem.sliceTo(path, "/"); + const first_element: []const u8 = std.mem.sliceTo(path, '/'); var search_slice = entries; var idx: usize = undefined; while (search_slice.len > 0) { - idx = search_slice / 2; + idx = search_slice.len / 2; const middle = search_slice[idx]; switch (std.mem.order(u8, first_element, middle.name)) { .eq => break, diff --git a/src/inode.zig b/src/inode.zig index f77dd90..0d3035e 100644 --- a/src/inode.zig +++ b/src/inode.zig @@ -224,7 +224,7 @@ const Tables = struct { pub fn extract(self: Inode, alloc: std.mem.Allocator, io: Io, fil: OffsetFile, super: Archive.Superblock, path: []const u8, options: ExtractionOptions) !void { var decomp = switch (super.compression) { .gzip => try @import("decomp/zlib.zig").init(alloc, super.block_size), - .lzma => try @import("decomp/lzma.zig").init(alloc, super.block_sizee), + .lzma => try @import("decomp/lzma.zig").init(alloc, super.block_size), .xz => try @import("decomp/xz.zig").init(alloc, super.block_size), .zstd => try @import("decomp/zstd.zig").init(alloc, super.block_size), else => unreachable, @@ -240,15 +240,33 @@ pub fn extract(self: Inode, alloc: std.mem.Allocator, io: Io, fil: OffsetFile, s defer que.close(io); switch (self.hdr.inode_type) { - .dir, .ext_dir => group.async(io, extractDir, .{ self, alloc, io, fil, super, path, options, &que }), - .file, .ext_file => group.async(io, extractRegFile, .{ self, alloc, io, file, super, path, options, &que }), - .symlink, .ext_symlink => group.async(Io, extractSymlink, .{ self, alloc, io, super, path, options, &que }), - .char_dev, - .block_dev, - .ext_char_dev, - .ext_block_dev, - => group.async(io, extractDevice, .{ self, alloc, io, super, path, options, &que }), - else => group.async(io, extractIPC, .{ self, alloc, io, super, path, options, &que }), + .dir, .ext_dir => group.async(io, extractDir, .{ + self, + alloc, + io, + fil, + &decomp.interface, + &frag_table, + super.block_size, + super.dir_start, + path, + options, + &que, + }), + .file, .ext_file => group.async(io, extractRegFile, .{ + self, + alloc, + io, + file, + &decomp.interface, + &frag_table, + super.block_size, + path, + options, + &que, + }), + .symlink, .ext_symlink => group.async(Io, extractSymlink, .{ self, io, path, options, &que }), + else => group.async(io, extractDevice, .{ self, alloc, io, super, path, options, &que }), } var id_table: LookupTable.CachedTable(u16) = .init(alloc, fil, decomp, super.id_start, super.id_count); @@ -271,7 +289,7 @@ pub fn extract(self: Inode, alloc: std.mem.Allocator, io: Io, fil: OffsetFile, s // TODO: Check error. const xattr_res = std.os.linux.fsetxattr(ret_file.handle, x.key, x.value.ptr, x.value.len, 0); if (xattr_res != 0 and options.verbose) - options.verbose_writer.?.print("setxattr failed with code: {}\n", .{xattr_res}); + options.verbose_writer.?.print("setxattr failed with code: {}\n", .{xattr_res}) catch {}; } } } @@ -279,7 +297,7 @@ pub fn extract(self: Inode, alloc: std.mem.Allocator, io: Io, fil: OffsetFile, s try ret_file.setPermissions(io, inode.hdr.permissions); try ret_file.setOwner(io, try id_table.get(io, inode.hdr.uid_idx), try id_table.get(io, inode.hdr.gid_idx)); } - if (group.token.raw == null and !que.type_erased.closed) que.close(io); + if (!que.type_erased.closed and group.token.raw == null) que.close(io); } } pub fn extractDir( @@ -288,12 +306,70 @@ pub fn extractDir( io: Io, fil: OffsetFile, decomp: *const Decompressor, + frag: *LookupTable.CachedTable(FragEntry), block_size: u32, dir_start: u64, + inode_start: u64, path: []const u8, options: ExtractionOptions, que: *Io.Queue(FileRet), -) !void {} +) !void { + defer alloc.free(path); + + const dirs = try self.readDirectory(alloc, io, fil, decomp, dir_start); + defer { + for (dirs) |d| + d.deinit(alloc); + alloc.free(dirs); + } + + var group: Io.Group = .init; + defer group.cancel(io); + + for (dirs) |d| { + var rdr = try fil.readerAt(io, d.block_start + inode_start, &[0]u8{}); + var meta: MetadataReader = .init(alloc, &rdr.interface, decomp); + try meta.interface.discardAll(d.block_offset); + + const inode = try read(alloc, &meta.interface, block_size); + + const new_path = try std.mem.concat(alloc, u8, &[_][]const u8{ path, "/", d.name }); + + switch (inode.hdr.inode_type) { + .dir, .ext_dir => group.async(io, extractDir, .{ + self, + alloc, + io, + fil, + &decomp.interface, + &frag, + block_size, + dir_start, + new_path, + options, + &que, + }), + .file, .ext_file => group.async(io, extractRegFile, .{ + self, + alloc, + io, + file, + &decomp.interface, + &frag, + block_size, + new_path, + options, + &que, + }), + .symlink, .ext_symlink => group.async(Io, extractSymlink, .{ self, alloc, io, new_path, options, &que }), + else => group.async(io, extractDevice, .{ self, alloc, io, new_path, options, &que }), + } + } + + try group.await(io); + + try que.putOne(io, .{ .file = try Io.Dir.cwd().openFile(io, path, .{}), .inode = self }); +} pub fn extractRegFile( self: Inode, alloc: std.mem.Allocator, @@ -306,6 +382,9 @@ pub fn extractRegFile( options: ExtractionOptions, que: *Io.Queue(FileRet), ) !void { + _ = options; + defer alloc.free(path); + const atom = try Io.Dir.cwd().createFileAtomic(io, path, .{}); defer atom.deinit(io); @@ -332,7 +411,7 @@ pub fn extractRegFile( else => unreachable, } - const ext: DataExtractor = .init(fil, cache, decomp, block_size, size, start, blocks); + const ext: DataExtractor = .init(fil, decomp, block_size, size, start, blocks); ext.addFrag(frag_offset, try frag.get(io, frag_idx)); var group: Io.Group = .init; @@ -353,7 +432,18 @@ pub fn extractSymlink( path: []const u8, options: ExtractionOptions, que: *Io.Queue(FileRet), -) !void {} +) !void { + defer alloc.free(path); + + _ = options; + _ = que; + // TODO: handle symlink options + const target = try self.symlinkTarget(); + + try Io.Dir.cwd().symLink(io, target, path, .{}); + + // TODO: On Linux you can't set permission & xattrs on symlinks (they inherit from their target), but on Mac you can. +} pub fn extractDevice( self: Inode, alloc: std.mem.Allocator, @@ -361,15 +451,45 @@ pub fn extractDevice( path: []const u8, options: ExtractionOptions, que: *Io.Queue(FileRet), -) !void {} -pub fn extractIPC( - self: Inode, - alloc: std.mem.Allocator, - io: Io, - path: []const u8, - options: ExtractionOptions, - que: *Io.Queue(FileRet), -) !void {} +) !void { + defer alloc.free(path); + + var dev: u32 = 0; + var mode: u32 = undefined; + + switch (self.data) { + .char_dev => |d| { + dev = d.dev; + mode = std.posix.DT.CHR; + }, + .block_dev => |d| { + dev = d.dev; + mode = std.posix.DT.BLK; + }, + .ext_char_dev => |d| { + dev = d.dev; + mode = std.posix.DT.BLK; + }, + .ext_block_dev => |d| { + dev = d.dev; + mode = std.posix.DT.BLK; + }, + .fifo, .ext_fifo => mode = std.posix.DT.FIFO, + .socket, .ext_socket => mode = std.posix.DT.SOCK, + else => unreachable, + } + + const sentinel_path = try std.mem.concatMaybeSentinel(alloc, u8, &[1][]const u8{path}, 0); + defer alloc.free(sentinel_path); + const res = std.os.linux.mknod(sentinel_path, mode, dev); + if (res != 0 and options.verbose) + options.verbose_writer.?.print("mknod failed with code: {}\n", .{res}) catch {}; + + try que.putOne(io, .{ + .file = try Io.Dir.cwd().openFile(io, path, .{}), + .inode = self, + }); +} fn applyMetadataLoop(alloc: std.mem.Allocator, io: Io, fil: OffsetFile, decomp: *const Decompressor, super: Archive.Superblock, que: *Io.Queue(FileRet), options: ExtractionOptions) !void { var id_table: LookupTable.CachedTable(u16) = .init(alloc, fil, decomp, super.id_start, super.id_count); diff --git a/src/util/data_extractor.zig b/src/util/data_extractor.zig index 80cbca0..eb0bbdc 100644 --- a/src/util/data_extractor.zig +++ b/src/util/data_extractor.zig @@ -12,7 +12,7 @@ const SharedCache = @import("shared_cache.zig"); const DataExtractor = @This(); fil: OffsetFile, -cache: *SharedCache, +// cache: *SharedCache, decomp: *const Decompressor, block_size: u32, @@ -23,10 +23,10 @@ blocks: []BlockSize, frag_offset: u32 = 0, frag_entry: ?FragEntry = null, -pub fn init(fil: OffsetFile, cache: *SharedCache, decomp: *const Decompressor, block_size: u32, file_size: u64, data_start: u64, blocks: []BlockSize) DataExtractor { +pub fn init(fil: OffsetFile, decomp: *const Decompressor, block_size: u32, file_size: u64, data_start: u64, blocks: []BlockSize) DataExtractor { return .{ .fil = fil, - .cache = cache, + // .cache = cache, .decomp = decomp, .block_size = block_size, diff --git a/src/util/data_reader.zig b/src/util/data_reader.zig index 59d3adc..3a04e8f 100644 --- a/src/util/data_reader.zig +++ b/src/util/data_reader.zig @@ -18,7 +18,7 @@ alloc: std.mem.Allocator, fil: OffsetFile, io: Io, -cache: *SharedCache, +// cache: *SharedCache, decomp: *const Decompressor, block_size: u32, @@ -34,7 +34,7 @@ sparse_block: bool = false, interface: Io.Reader, -pub fn init(alloc: std.mem.Allocator, io: Io, fil: OffsetFile, cache: *SharedCache, decomp: *const Decompressor, block_size: u32, file_size: u64, data_start: u64, blocks: []BlockSize) !DataReader { +pub fn init(alloc: std.mem.Allocator, io: Io, fil: OffsetFile, decomp: *const Decompressor, block_size: u32, file_size: u64, data_start: u64, blocks: []BlockSize) !DataReader { return .{ .alloc = alloc, @@ -48,7 +48,7 @@ pub fn init(alloc: std.mem.Allocator, io: Io, fil: OffsetFile, cache: *SharedCac .blocks = blocks, .interface = .{ - .buffer = try cache.getCache(io), + .buffer = try alloc.alloc(u8, block_size), .seek = 0, .end = 0, .vtable = &.{ @@ -60,10 +60,7 @@ pub fn init(alloc: std.mem.Allocator, io: Io, fil: OffsetFile, cache: *SharedCac }; } pub fn deinit(self: *DataReader) void { - if (self.interface.buffer.len > 0) { - const buf_nod: *SharedCache.BufferNode = @fieldParentPtr("cache", self.interface.buffer); - self.cache.returnCache(buf_nod); - } + self.alloc.free(self.interface.buffer); } pub fn addFrag(self: *DataReader, frag_offset: u32, entry: FragEntry) void { self.frag_offset = frag_offset;