diff --git a/src/file.zig b/src/file.zig index bd5fa5b..06ee858 100644 --- a/src/file.zig +++ b/src/file.zig @@ -85,36 +85,7 @@ pub fn open(self: File, alloc: std.mem.Allocator, io: Io, filepath: []const u8) } pub fn extract(self: File, alloc: std.mem.Allocator, io: Io, filepath: []const u8, options: ExtractionOptions) !void { - var cache: SharedCache = try .init(alloc, 10); // TODO: calculate a good initial cache size. - defer cache.deinit(); - var decomp = switch (self.archive.super.compression) { - .gzip => {}, - .lzma => {}, - .xz => {}, - .zstd => {}, - else => unreachable, - }; - return self.extractReal(alloc, io, &cache, &decomp.interface, filepath, options); -} -fn extractReal(self: File, alloc: std.mem.Allocator, io: Io, cache: *SharedCache, decomp: *const Decompressor, filepath: []const u8, options: ExtractionOptions) !void { - _ = options; - switch (self.inode.hdr.inode_type) { - .file, .ext_file => { - var ext = try self.inode.dataExtractor( - self.archive.file, - cache, - decomp, - self.archive.super.block_size, - ); - - var atomic_file = try Io.Dir.cwd().createFileAtomic(io, filepath, .{}); - defer atomic_file.deinit(io); - - try ext.extract(alloc, io, atomic_file.file); - try atomic_file.link(io); - }, - else => return error.TODO, - } + return self.inode.extract(alloc, io, self.archive.file, self.archive.super, filepath, options); } // Types diff --git a/src/inode.zig b/src/inode.zig index 6a22167..f77dd90 100644 --- a/src/inode.zig +++ b/src/inode.zig @@ -7,6 +7,7 @@ const Io = std.Io; const Archive = @import("archive.zig"); const DirEntry = @import("directory.zig"); const ExtractionOptions = @import("options.zig"); +const FragEntry = @import("frag.zig").FragEntry; const dir = @import("inode_data/dir.zig"); const file = @import("inode_data/file.zig"); const misc = @import("inode_data/misc.zig"); @@ -122,6 +123,18 @@ pub fn gid(self: Inode, alloc: std.mem.Allocator, io: Io, fil: OffsetFile, decom pub fn uid(self: Inode, alloc: std.mem.Allocator, io: Io, fil: OffsetFile, decomp: *const Decompressor, id_table_start: u64) !u16 { return LookupTable.lookupValue(u16, alloc, io, decomp, fil, id_table_start, self.hdr.uid_idx); } +pub fn xattrIndex(self: Inode) !u32 { + return switch (self.data) { + .ext_dir => |e| e.xattr_idx, + .ext_file => |e| e.xattr_idx, + .ext_symlink => |e| e.xattr_idx, + .ext_block_dev => |e| e.xattr_idx, + .ext_char_dev => |e| e.xattr_idx, + .ext_fifo => |e| e.xattr_idx, + .ext_socket => |e| e.xattr_idx, + else => Error.NotExtended, + }; +} // Get an inode's xattr values. If the inode does not have xattr values (including if the inode is not an extended type), an empty slice is returned. pub fn xattrValues(self: Inode, alloc: std.mem.Allocator, io: Io, fil: OffsetFile, decomp: *const Decompressor, xattr_table_start: u64) ![]XattrTable.XattrOwned { const idx = switch (self.data) { @@ -198,9 +211,189 @@ pub const Header = extern struct { // Extract -pub fn extract(self: Inode, alloc: std.mem.Allocator, io: Io, fil: OffsetFile, super: Archive.Superblock, path: []const u8, options: ExtractionOptions) !void {} -pub fn extractDir(self: Inode, alloc: std.mem.Allocator, io: Io, fil: OffsetFile, super: Archive.Superblock, path: []const u8, options: ExtractionOptions) !void {} -pub fn extractRegFile(self: Inode, alloc: std.mem.Allocator, io: Io, fil: OffsetFile, super: Archive.Superblock, path: []const u8, options: ExtractionOptions) !void {} -pub fn extractSymlink(self: Inode, alloc: std.mem.Allocator, io: Io, fil: OffsetFile, super: Archive.Superblock, path: []const u8, options: ExtractionOptions) !void {} -pub fn extractDevice(self: Inode, alloc: std.mem.Allocator, io: Io, fil: OffsetFile, super: Archive.Superblock, path: []const u8, options: ExtractionOptions) !void {} -pub fn extractIPC(self: Inode, alloc: std.mem.Allocator, io: Io, fil: OffsetFile, super: Archive.Superblock, path: []const u8, options: ExtractionOptions) !void {} +const FileRet = struct { + file: Io.File, + inode: Inode, +}; +const Tables = struct { + id: LookupTable.CachedTable(u16), + frag: LookupTable.CachedTable(FragEntry), + xattr: XattrTable, +}; + +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), + .xz => try @import("decomp/xz.zig").init(alloc, super.block_size), + .zstd => try @import("decomp/zstd.zig").init(alloc, super.block_size), + else => unreachable, + }; + defer decomp.deinit(); + + var frag_table: LookupTable.CachedTable(FragEntry) = .init(alloc, fil, &decomp.interface, super.frag_start, super.frag_count); + defer frag_table.deinit(io); + + var group: Io.Group = .init; + defer group.cancel(io); + var que: Io.Queue(FileRet) = .init(&[1]FileRet{undefined} ** 12); + 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 }), + } + + var id_table: LookupTable.CachedTable(u16) = .init(alloc, fil, decomp, super.id_start, super.id_count); + defer id_table.deinit(io); + var xattr_table: XattrTable = try .init(alloc, io, fil, decomp, super.xattr_start); + defer xattr_table.deinit(io); + + for (que.getOne(io)) |res| { + const ret = res catch break; + + const inode: Inode = ret.inode; + defer inode.deinit(alloc); + const ret_file: Io.File = ret.file; + defer ret_file.close(io); + + if (!options.ignore_xattr) { + if (inode.xattrIndex()) |idx| { + const xattrs = try xattr_table.get(io, idx); + for (xattrs) |x| { + // 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}); + } + } + } + if (!options.ignore_permissions) { + 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); + } +} +pub fn extractDir( + self: Inode, + alloc: std.mem.Allocator, + io: Io, + fil: OffsetFile, + decomp: *const Decompressor, + block_size: u32, + dir_start: u64, + path: []const u8, + options: ExtractionOptions, + que: *Io.Queue(FileRet), +) !void {} +pub fn extractRegFile( + self: Inode, + alloc: std.mem.Allocator, + io: Io, + fil: OffsetFile, + decomp: *const Decompressor, + frag: *LookupTable.CachedTable(FragEntry), + block_size: u32, + path: []const u8, + options: ExtractionOptions, + que: *Io.Queue(FileRet), +) !void { + const atom = try Io.Dir.cwd().createFileAtomic(io, path, .{}); + defer atom.deinit(io); + + var size: u64 = undefined; + var start: u64 = undefined; + var blocks: []file.BlockSize = undefined; + var frag_idx: u32 = undefined; + var frag_offset: u32 = undefined; + switch (self.data) { + .file => |f| { + size = f.size; + start = f.block_start; + blocks = f.block_sizes; + frag_idx = f.frag_idx; + frag_offset = f.frag_block_offset; + }, + .ext_file => |f| { + size = f.size; + start = f.block_start; + blocks = f.block_sizes; + frag_idx = f.frag_idx; + frag_offset = f.frag_block_offset; + }, + else => unreachable, + } + + const ext: DataExtractor = .init(fil, cache, decomp, block_size, size, start, blocks); + ext.addFrag(frag_offset, try frag.get(io, frag_idx)); + + var group: Io.Group = .init; + defer group.cancel(io); + + ext.extractAsync(alloc, io, &group, atom.file); + + try group.await(io); + + try atom.link(io); + + try que.putOne(io, .{ .file = atom.file, .inode = self }); +} +pub fn extractSymlink( + self: Inode, + alloc: std.mem.Allocator, + io: Io, + path: []const u8, + options: ExtractionOptions, + que: *Io.Queue(FileRet), +) !void {} +pub fn extractDevice( + self: Inode, + alloc: std.mem.Allocator, + io: Io, + 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 {} + +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); + defer id_table.deinit(io); + var xattr_table: XattrTable = try .init(alloc, io, fil, decomp, super.xattr_start); + defer xattr_table.deinit(io); + for (try que.getOne(io)) |ret| { + const inode: Inode = ret.inode; + defer inode.deinit(alloc); + const ret_file: Io.File = ret.file; + defer ret_file.close(io); + + if (!options.ignore_xattr) { + if (inode.xattrIndex()) |idx| { + const xattrs = try xattr_table.get(io, idx); + for (xattrs) |x| { + // TODO: Check error. + _ = std.os.linux.fsetxattr(ret_file.handle, x.key, x.value.ptr, x.value.len, 0); + } + } + } + if (!options.ignore_permissions) { + 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)); + } + } +} diff --git a/src/lookup_table.zig b/src/lookup_table.zig index 29db239..f2480b4 100644 --- a/src/lookup_table.zig +++ b/src/lookup_table.zig @@ -32,6 +32,8 @@ pub fn CachedTable(comptime T: anytype) type { alloc: std.mem.Allocator, fil: OffsetFile, + decomp: *const Decompressor, + table_start: u64, total_num: u32, @@ -39,10 +41,12 @@ pub fn CachedTable(comptime T: anytype) type { mut: Io.Mutex = .init, - pub fn init(alloc: std.mem.Allocator, fil: OffsetFile, offset: u64, total_num: u32) Table { + pub fn init(alloc: std.mem.Allocator, fil: OffsetFile, decomp: *const Decompressor, offset: u64, total_num: u32) Table { return .{ .alloc = alloc, .fil = fil, + .decomp = decomp, + .table_start = offset, .total_num = total_num, diff --git a/src/xattr_table.zig b/src/xattr_table.zig index f72f4d7..e97cb91 100644 --- a/src/xattr_table.zig +++ b/src/xattr_table.zig @@ -40,8 +40,8 @@ pub fn init(alloc: std.mem.Allocator, io: Io, fil: OffsetFile, decomp: *const De .value_cache = .init(alloc), }; } -pub fn deinit(self: *XattrCachedTable) void { - self.table.deinit(); +pub fn deinit(self: *XattrCachedTable, io: Io) void { + self.table.deinit(io); self.value_cache.deinit(); }