diff --git a/README.md b/README.md index 7ec974f..53c64bb 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ Most features are present except for the following: This is some basic observation's I've made about this library's performance when compared to `unsquashfs`. Unless otherwise stated, most observations were made when extracting my test archive (which is fairly small and uses zstd compression) and with `--release=fast`. -* Under ideal circumstances, my library is ~60% slower (.12s vs .19s). +* Under ideal circumstances, my library is ~70% slower (.12s vs .20s). * Using Zig decompression libraries *significantly* increases decompression time by ~600%. Under ideal circumstances. * Performance improvements/regressions will be common. I'm still learning Zig. @@ -45,8 +45,8 @@ Example Times: * *unsquashfs, multi-threaded*: .12s * *unsquashfs, single-threaded*: .13s -* *C-libs, single-threaded*: .56s -* *C-libs, multi-threaded*: .19s +* *C-libs, single-threaded*: .45s +* *C-libs, multi-threaded*: .20s * *Zig-libs, single-threaded*: 5.78s * *Zig-libs, multi-threaded*: 1.08s diff --git a/build.zig b/build.zig index d27f8f9..eec0868 100644 --- a/build.zig +++ b/build.zig @@ -21,12 +21,12 @@ pub fn build(b: *std.Build) !void { }); mod.addOptions("config", zig_squashfs_options); if (use_c_libs_option == true) { - mod.linkSystemLibrary("zlib", .{}); - mod.linkSystemLibrary("lzma", .{}); + mod.linkSystemLibrary("zlib", .{ .preferred_link_mode = .static }); + mod.linkSystemLibrary("lzma", .{ .preferred_link_mode = .static }); if (allow_lzo == true) - mod.linkSystemLibrary("minilzo", .{}); - mod.linkSystemLibrary("lz4", .{}); - mod.linkSystemLibrary("zstd", .{}); + mod.linkSystemLibrary("minilzo", .{ .preferred_link_mode = .static }); + mod.linkSystemLibrary("lz4", .{ .preferred_link_mode = .static }); + mod.linkSystemLibrary("zstd", .{ .preferred_link_mode = .static }); } var version = version_string_option orelse "0.0.0-testing"; @@ -52,7 +52,7 @@ pub fn build(b: *std.Build) !void { const exe = b.addExecutable(.{ .name = "unsquashfs", .root_module = exe_mod, - .use_llvm = true, + // .use_llvm = true, This can be needed to properly debug }); const lib = b.addLibrary(.{ diff --git a/src/archive.zig b/src/archive.zig index c19c635..f053e0b 100644 --- a/src/archive.zig +++ b/src/archive.zig @@ -12,7 +12,7 @@ const InodeRef = Inode.Ref; const BlockSize = @import("inode_data/file.zig").BlockSize; const SfsFile = @import("file.zig"); const Superblock = @import("super.zig").Superblock; -const Table = @import("table.zig").Table; +const Tables = @import("tables.zig"); const MetadataReader = @import("util/metadata.zig"); const OffsetFile = @import("util/offset_file.zig"); const XattrTable = @import("xattr.zig"); @@ -22,14 +22,6 @@ const config = if (builtin.is_test) .{ .allow_lzo = false, } else @import("config"); -/// Information about a fragment section. Multiple fragments are contained in the block described by a single FragEntry. -/// The offset into the block and fragment size is stored in the file's inode. -pub const FragEntry = packed struct { - start: u64, - size: BlockSize, - _: u32, -}; - const Archive = @This(); alloc: std.mem.Allocator, @@ -39,10 +31,7 @@ decomp: Decomp.DecompFn, super: Superblock, -frag_table: Table(FragEntry), -id_table: Table(u16), -export_table: Table(InodeRef), -xattr_table: XattrTable, +tables: ?Tables = null, /// Default settings using std.Thread.getCpuCount() threads and the minimum of 4gb or half of system memory for memory usage. pub fn init(alloc: std.mem.Allocator, fil: File, offset: u64) !Archive { @@ -64,19 +53,16 @@ pub fn init(alloc: std.mem.Allocator, fil: File, offset: u64) !Archive { .fil = off_fil, .decomp = decomp, .super = super, - .frag_table = try .init(alloc, off_fil, decomp, super.frag_start, super.frag_count), - .id_table = try .init(alloc, off_fil, decomp, super.id_start, super.id_count), - .export_table = try .init(alloc, off_fil, decomp, super.export_start, super.inode_count), - .xattr_table = try .init(alloc, off_fil, decomp, super.xattr_start), }; } pub fn deinit(self: *Archive) void { - self.frag_table.deinit(); - self.export_table.deinit(); - self.id_table.deinit(); + if (self.tables != null) + self.tables.?.deinit(); } pub fn inode(self: *Archive, alloc: std.mem.Allocator, num: u32) !Inode { + if (self.tables == null) + self.tables = try .init(alloc, self); const ref = try self.export_table.get(num - 1); var rdr = try self.fil.readerAt(ref.block_start + self.super.inode_start, &[0]u8{}); var meta: MetadataReader = .init(alloc, &rdr.interface, &self.decomp); @@ -85,6 +71,8 @@ pub fn inode(self: *Archive, alloc: std.mem.Allocator, num: u32) !Inode { } pub fn root(self: *Archive, alloc: std.mem.Allocator) !SfsFile { + if (self.tables == null) + self.tables = try .init(alloc, self); var rdr = try self.fil.readerAt(self.super.root_ref.block_start + self.super.inode_start, &[0]u8{}); var meta: MetadataReader = .init(alloc, &rdr.interface, self.decomp); try meta.interface.discardAll(self.super.root_ref.block_offset); @@ -93,12 +81,14 @@ pub fn root(self: *Archive, alloc: std.mem.Allocator) !SfsFile { } pub fn open(self: *Archive, alloc: std.mem.Allocator, path: []const u8) !SfsFile { + if (self.tables == null) + self.tables = try .init(alloc, self); var root_fil = try self.root(alloc); defer if (!SfsFile.pathIsSelf(path)) root_fil.deinit(); return root_fil.open(path); } -pub fn extract(self: *Archive, alloc: std.mem.Allocator, path: []const u8, options: ExtractionOptions) !void { +pub fn extract(self: Archive, alloc: std.mem.Allocator, path: []const u8, options: ExtractionOptions) !void { var rdr = try self.fil.readerAt(self.super.root_ref.block_start + self.super.inode_start, &[0]u8{}); var meta: MetadataReader = .init(self.alloc, &rdr.interface, self.decomp); try meta.interface.discardAll(self.super.root_ref.block_offset); diff --git a/src/inode.zig b/src/inode.zig index cb24c4b..58687b8 100644 --- a/src/inode.zig +++ b/src/inode.zig @@ -12,6 +12,7 @@ const ExtractionOptions = @import("options.zig"); const dir = @import("inode_data/dir.zig"); const file = @import("inode_data/file.zig"); const misc = @import("inode_data/misc.zig"); +const Tables = @import("tables.zig"); const DataReader = @import("util/data.zig"); const ThreadedDataReader = @import("util/data_threaded.zig"); const InodeExtract = @import("util/extract.zig"); @@ -96,7 +97,7 @@ pub fn read(alloc: std.mem.Allocator, rdr: *Reader, block_size: u32) !Inode { }, }; } -pub fn readFromEntry(alloc: std.mem.Allocator, archive: *Archive, entry: DirEntry) !Inode { +pub fn readFromEntry(alloc: std.mem.Allocator, archive: Archive, entry: DirEntry) !Inode { var rdr = try archive.fil.readerAt(archive.super.inode_start + entry.block_start, &[0]u8{}); var meta: MetadataReader = .init(alloc, &rdr.interface, archive.decomp); try meta.interface.discardAll(entry.block_offset); @@ -114,17 +115,17 @@ pub fn deinit(self: Inode, alloc: std.mem.Allocator) void { } /// Get the data reader for a file inode. -pub fn dataReader(self: Inode, alloc: std.mem.Allocator, archive: *Archive) !DataReader { +pub fn dataReader(self: Inode, alloc: std.mem.Allocator, archive: Archive, tables: *Tables) !DataReader { return switch (self.hdr.inode_type) { - .file => readerFromData(alloc, archive, self.data.file), - .ext_file => readerFromData(alloc, archive, self.data.ext_file), + .file => readerFromData(alloc, archive, tables, self.data.file), + .ext_file => readerFromData(alloc, archive, tables, self.data.ext_file), else => error.NotRegularFile, }; } -fn readerFromData(alloc: std.mem.Allocator, archive: *Archive, data: anytype) !DataReader { - var out: DataReader = .init(alloc, archive.*, data.block_sizes, data.block_start, data.size); +fn readerFromData(alloc: std.mem.Allocator, archive: Archive, tables: *Tables, data: anytype) !DataReader { + var out: DataReader = .init(alloc, archive, data.block_sizes, data.block_start, data.size); if (data.frag_idx != 0xFFFFFFFF) - out.addFragment(try archive.frag_table.get(data.frag_idx), data.frag_block_offset); + out.addFragment(try tables.frag_table.get(data.frag_idx), data.frag_block_offset); return out; } @@ -157,33 +158,32 @@ pub fn xattrIdx(self: Inode) u32 { /// Applies the Inode's metadata to the given File. /// Mod time is always set, but permissions and xattrs are set based on the given ExtractionOptions. -pub fn setMetadata(self: Inode, alloc: std.mem.Allocator, archive: *Archive, fil: std.fs.File, options: ExtractionOptions) !void { +pub fn setMetadata(self: Inode, alloc: std.mem.Allocator, tables: *Tables, fil: std.fs.File, options: ExtractionOptions) !void { const time = @as(i128, self.hdr.mod_time) * 1000000000; try fil.updateTimes(time, time); if (!options.ignore_permissions) { try fil.chmod(self.hdr.permissions); - try fil.chown(try archive.id_table.get(self.hdr.uid_idx), try archive.id_table.get(self.hdr.gid_idx)); + try fil.chown(try tables.id_table.get(self.hdr.uid_idx), try tables.id_table.get(self.hdr.gid_idx)); } if (!options.ignore_xattr) { const idx = self.xattrIdx(); if (idx == 0xFFFFFFFF) return; - const xattrs = try archive.xattr_table.get(alloc, idx); + const xattrs = try tables.xattr_table.get(alloc, idx); defer alloc.free(xattrs); for (xattrs) |kv| { - const res = std.os.linux.fsetxattr(fil.handle, @ptrCast(kv.key), @ptrCast(kv.value), kv.value.len, 0); - alloc.free(kv.key); - alloc.free(kv.value); + const res = std.os.linux.fsetxattr(fil.handle, kv.key[0.. :0], kv.value.ptr, kv.value.len, 0); if (res != 0) { if (options.verbose) options.verbose_writer.?.print("fsetxattr has result of: {}\n", .{res}) catch {}; - //TODO: Currently this seems a bit flakey, so we just ignore the result... for now. - // return error.SetXattr; + return error.SetXattr; } + alloc.free(kv.key); + alloc.free(kv.value); } } } /// Extract the inode to the given path. -pub fn extractTo(self: Inode, alloc: std.mem.Allocator, archive: *Archive, path: []const u8, options: ExtractionOptions) !void { +pub fn extractTo(self: Inode, alloc: std.mem.Allocator, archive: Archive, path: []const u8, options: ExtractionOptions) !void { return InodeExtract.extractTo(alloc, self, archive, path, options); } diff --git a/src/table.zig b/src/tables.zig similarity index 64% rename from src/table.zig rename to src/tables.zig index c98e85d..700d3b6 100644 --- a/src/table.zig +++ b/src/tables.zig @@ -1,14 +1,45 @@ const std = @import("std"); const Mutex = std.Thread.Mutex; +const Archive = @import("archive.zig"); const DecompFn = @import("decomp.zig").DecompFn; +const BlockSize = @import("inode_data/file.zig").BlockSize; +const InodeRef = @import("inode.zig").Ref; +const Superblock = @import("super.zig").Superblock; const MetadataReader = @import("util/metadata.zig"); const OffsetFile = @import("util/offset_file.zig"); +const XattrTable = @import("xattr.zig"); -const TableError = error{ - InvalidIndex, +/// Information about a fragment section. Multiple fragments are contained in the block described by a single FragEntry. +/// The offset into the block and fragment size is stored in the file's inode. +pub const FragEntry = packed struct { + start: u64, + size: BlockSize, + _: u32, }; +const Tables = @This(); + +frag_table: Table(FragEntry), +id_table: Table(u16), +export_table: Table(InodeRef), +xattr_table: XattrTable, + +pub fn init(alloc: std.mem.Allocator, archive: Archive) !Tables { + return .{ + .frag_table = try .init(alloc, archive.fil, archive.decomp, archive.super.frag_start, archive.super.frag_count), + .id_table = try .init(alloc, archive.fil, archive.decomp, archive.super.id_start, archive.super.id_count), + .export_table = try .init(alloc, archive.fil, archive.decomp, archive.super.export_start, archive.super.inode_count), + .xattr_table = try .init(alloc, archive.fil, archive.decomp, archive.super.xattr_start), + }; +} +pub fn deinit(self: *Tables) void { + self.frag_table.deinit(); + self.id_table.deinit(); + self.export_table.deinit(); + self.xattr_table.deinit(); +} + /// A two-layer metadata table. pub fn Table(T: anytype) type { return struct { @@ -47,7 +78,7 @@ pub fn Table(T: anytype) type { } pub fn get(self: *Self, idx: u32) !T { - if (idx >= self.values) return TableError.InvalidIndex; + if (idx >= self.values) return error.InvalidIndex; const block_num = idx / VALS_PER_BLOCK; const idx_offset = idx - (block_num * VALS_PER_BLOCK); if (self.tab.contains(block_num)) { diff --git a/src/util/data.zig b/src/util/data.zig index 177e7c1..0c1d43e 100644 --- a/src/util/data.zig +++ b/src/util/data.zig @@ -6,9 +6,9 @@ const Writer = std.Io.Writer; const Limit = std.Io.Limit; const Archive = @import("../archive.zig"); -const FragEntry = Archive.FragEntry; const DecompFn = @import("../decomp.zig").DecompFn; const BlockSize = @import("../inode_data/file.zig").BlockSize; +const FragEntry = @import("../tables.zig").FragEntry; const OffsetFile = @import("offset_file.zig"); const DataReader = @This(); diff --git a/src/util/data_threaded.zig b/src/util/data_threaded.zig index 784ed5d..280586a 100644 --- a/src/util/data_threaded.zig +++ b/src/util/data_threaded.zig @@ -8,9 +8,9 @@ const WaitGroup = std.Thread.WaitGroup; const Pool = std.Thread.Pool; const Archive = @import("../archive.zig"); -const FragEntry = Archive.FragEntry; const DecompFn = @import("../decomp.zig").DecompFn; const BlockSize = @import("../inode_data/file.zig").BlockSize; +const FragEntry = @import("../tables.zig").FragEntry; const InodeFinish = @import("inode_finish.zig"); const OffsetFile = @import("offset_file.zig"); diff --git a/src/util/extract.zig b/src/util/extract.zig index 20ea222..285997c 100644 --- a/src/util/extract.zig +++ b/src/util/extract.zig @@ -7,6 +7,7 @@ const Archive = @import("../archive.zig"); const DirEntry = @import("../dir_entry.zig"); const Inode = @import("../inode.zig"); const ExtractionOptions = @import("../options.zig"); +const Tables = @import("../tables.zig"); const InodeFinish = @import("inode_finish.zig"); const FinishUnion = InodeFinish.FinishUnion; const ThreadedDataReader = @import("data_threaded.zig"); @@ -17,7 +18,7 @@ const STACK_ALLOC_SIZE = 1024 * 1024; pub fn extractTo( allocator: Allocator, inode: Inode, - archive: *Archive, + archive: Archive, path: []const u8, options: ExtractionOptions, ) !void { @@ -27,11 +28,15 @@ pub fn extractTo( var stack_alloc = std.heap.stackFallback(STACK_ALLOC_SIZE, allocator); var arena: std.heap.ArenaAllocator = .init(stack_alloc.get()); defer arena.deinit(); - if (options.threads <= 1) - return extractSingleThread(arena.allocator(), inode, archive, path, options); + if (options.threads <= 1) { + const alloc = arena.allocator(); + var tables: Tables = try .init(alloc, archive); + return extractSingleThread(arena.allocator(), inode, archive, &tables, path, options); + } var thread_alloc = std.heap.ThreadSafeAllocator{ .child_allocator = arena.allocator() }; const alloc = thread_alloc.allocator(); + var tables: Tables = try .init(alloc, archive); var pool_alloc = std.heap.stackFallback(10 * 1024, alloc); var pool: Pool = undefined; @@ -44,6 +49,7 @@ pub fn extractTo( alloc, inode, archive, + &tables, path, options, &pool, @@ -57,7 +63,8 @@ pub fn extractTo( fn extractSingleThread( alloc: Allocator, inode: Inode, - archive: *Archive, + archive: Archive, + tables: *Tables, path: []const u8, options: ExtractionOptions, ) !void { @@ -73,37 +80,38 @@ fn extractSingleThread( // otherwise be more conscientious about freeing memory. // For now, this is good enough. - const entries = try inode.dirEntries(alloc, archive.*); + const entries = try inode.dirEntries(alloc, archive); for (entries) |ent| { const sub_inode: Inode = try .readFromEntry(alloc, archive, ent); const new_path = try std.mem.concat(alloc, u8, &[_][]const u8{ path, "/", ent.name }); - try extractSingleThread(alloc, sub_inode, archive, new_path, options); + try extractSingleThread(alloc, sub_inode, archive, tables, new_path, options); } const fil = try std.fs.cwd().openFile(path, .{}); defer fil.close(); - try inode.setMetadata(alloc, archive, fil, options); + try inode.setMetadata(alloc, tables, fil, options); }, .file, .ext_file => { var fil = try std.fs.cwd().createFile(path, .{ .exclusive = true }); defer fil.close(); var wrt = fil.writer(&[0]u8{}); - var dat_rdr = try inode.dataReader(alloc, archive); + var dat_rdr = try inode.dataReader(alloc, archive, tables); defer dat_rdr.deinit(); _ = try dat_rdr.interface.streamRemaining(&wrt.interface); try wrt.interface.flush(); - try inode.setMetadata(alloc, archive, fil, options); + try inode.setMetadata(alloc, tables, fil, options); }, .symlink, .ext_symlink => return extractSymlink(inode, path), - else => return extractDeviceAndIPC(inode, alloc, archive, path, options), + else => return extractDeviceAndIPC(inode, alloc, tables, path, options), } } fn extractMultiThread( alloc: Allocator, inode: Inode, - archive: *Archive, + archive: Archive, + tables: *Tables, path: []const u8, options: ExtractionOptions, pool: *Pool, @@ -130,17 +138,22 @@ fn extractMultiThread( // otherwise be more conscientious about freeing memory. // For now, this is good enough. - const entries = inode.dirEntries(alloc, archive.*) catch |res_err| { + const entries = inode.dirEntries(alloc, archive) catch |res_err| { err.* = res_err; fin.finish(); return; }; + if (entries.len == 0) { + fin.finish(); + return; + } + var dir_fin = InodeFinish.create( alloc, inode, path, - archive, + tables, options, fin, err, @@ -153,21 +166,24 @@ fn extractMultiThread( }; for (entries) |ent| { - if (ent.inode_type == .dir) + if (ent.inode_type == .dir) { extractEntry( alloc, ent, archive, + tables, path, options, pool, .{ .fin = dir_fin }, err, ); + continue; + } pool.spawn( extractEntry, - .{ alloc, ent, archive, path, options, pool, FinishUnion{ .fin = dir_fin }, err }, + .{ alloc, ent, archive, tables, path, options, pool, FinishUnion{ .fin = dir_fin }, err }, ) catch |res_err| { err.* = res_err; dir_fin.finish(); @@ -184,23 +200,32 @@ fn extractMultiThread( return; }; - var data_rdr = threadedDataReader(inode, alloc, archive) catch |res_err| { + var data_rdr = threadedDataReader(inode, alloc, archive, tables) catch |res_err| { if (options.verbose) options.verbose_writer.?.print("Can't create data reader for inode #{} (extracting to {s}): {}\n", .{ inode.hdr.num, path, res_err }) catch {}; err.* = res_err; fin.finish(); return; }; + if (data_rdr == null) { + inode.setMetadata(alloc, tables, fil, options) catch |res_err| { + if (options.verbose) + options.verbose_writer.?.print("Can't set metadata to {s}: {}\n", .{ path, res_err }) catch {}; + err.* = res_err; + }; + fin.finish(); + return; + } const file_fin = InodeFinish.create( alloc, inode, path, - archive, + tables, options, fin, err, fil, - data_rdr.num_blocks, + data_rdr.?.num_blocks, ) catch |res_err| { if (options.verbose) options.verbose_writer.?.print("Can't create callback for inode #{} (extracting to {s}): {}\n", .{ inode.hdr.num, path, res_err }) catch {}; @@ -209,7 +234,7 @@ fn extractMultiThread( return; }; - data_rdr.extractThreaded(fil, pool, file_fin); + data_rdr.?.extractThreaded(fil, pool, file_fin); }, .symlink, .ext_symlink => { extractSymlink(inode, path) catch |res_err| { @@ -218,7 +243,7 @@ fn extractMultiThread( fin.finish(); }, else => { - extractDeviceAndIPC(inode, alloc, archive, path, options) catch |res_err| { + extractDeviceAndIPC(inode, alloc, tables, path, options) catch |res_err| { err.* = res_err; }; fin.finish(); @@ -229,7 +254,8 @@ fn extractMultiThread( fn extractEntry( alloc: Allocator, ent: DirEntry, - archive: *Archive, + archive: Archive, + tables: *Tables, path: []const u8, options: ExtractionOptions, pool: *Pool, @@ -247,21 +273,22 @@ fn extractEntry( fin.finish(); return; }; - extractMultiThread(alloc, inode, archive, new_path, options, pool, fin, err); + extractMultiThread(alloc, inode, archive, tables, new_path, options, pool, fin, err); } /// Get a threaded data reader for a file inode. -fn threadedDataReader(self: Inode, alloc: std.mem.Allocator, archive: *Archive) !ThreadedDataReader { +fn threadedDataReader(self: Inode, alloc: std.mem.Allocator, archive: Archive, tables: *Tables) !?ThreadedDataReader { return switch (self.hdr.inode_type) { - .file => threadedReaderFromData(alloc, archive, self.data.file), - .ext_file => threadedReaderFromData(alloc, archive, self.data.ext_file), + .file => threadedReaderFromData(alloc, archive, tables, self.data.file), + .ext_file => threadedReaderFromData(alloc, archive, tables, self.data.ext_file), else => error.NotRegularFile, }; } -fn threadedReaderFromData(alloc: std.mem.Allocator, archive: *Archive, data: anytype) !ThreadedDataReader { - var out: ThreadedDataReader = .init(alloc, archive.*, data.block_sizes, data.block_start, data.size); +fn threadedReaderFromData(alloc: std.mem.Allocator, archive: Archive, tables: *Tables, data: anytype) !?ThreadedDataReader { + if (data.block_sizes.len == 0 and data.frag_idx == 0xFFFFFFFF) return null; + var out: ThreadedDataReader = .init(alloc, archive, data.block_sizes, data.block_start, data.size); if (data.frag_idx != 0xFFFFFFFF) - out.addFragment(try archive.frag_table.get(data.frag_idx), data.frag_block_offset); + out.addFragment(try tables.frag_table.get(data.frag_idx), data.frag_block_offset); return out; } @@ -277,7 +304,7 @@ fn extractSymlink(self: Inode, path: []const u8) !void { } /// Creates the device described by the inode. /// Sets metadata. -fn extractDeviceAndIPC(self: Inode, alloc: std.mem.Allocator, archive: *Archive, path: []const u8, options: ExtractionOptions) !void { +fn extractDeviceAndIPC(self: Inode, alloc: std.mem.Allocator, tables: *Tables, path: []const u8, options: ExtractionOptions) !void { var mode: u32 = undefined; var dev: u32 = 0; switch (self.data) { @@ -322,5 +349,5 @@ fn extractDeviceAndIPC(self: Inode, alloc: std.mem.Allocator, archive: *Archive, } var fil = try std.fs.cwd().openFile(path, .{}); defer fil.close(); - try self.setMetadata(alloc, archive, fil, options); + try self.setMetadata(alloc, tables, fil, options); } diff --git a/src/util/inode_finish.zig b/src/util/inode_finish.zig index c6e57fc..c16dfd2 100644 --- a/src/util/inode_finish.zig +++ b/src/util/inode_finish.zig @@ -5,6 +5,7 @@ const Mutex = std.Thread.Mutex; const Archive = @import("../archive.zig"); const Inode = @import("../inode.zig"); const ExtractionOptions = @import("../options.zig"); +const Tables = @import("../tables.zig"); const InodeFinish = @This(); @@ -28,7 +29,7 @@ alloc: std.mem.Allocator, inode: Inode, path: []const u8, -archive: *Archive, +tables: *Tables, options: ExtractionOptions, parent_finish: FinishUnion, fil: ?std.fs.File, @@ -41,13 +42,15 @@ pub fn create( alloc: std.mem.Allocator, inode: Inode, path: []const u8, - archive: *Archive, + tables: *Tables, options: ExtractionOptions, parent_finish: FinishUnion, out_err: *?anyerror, fil: ?std.fs.File, work_size: usize, ) !*InodeFinish { + if (work_size == 0) + return error.InvalidWorkSize; const out = try alloc.create(InodeFinish); errdefer alloc.destroy(out); out.* = .{ @@ -55,7 +58,7 @@ pub fn create( .inode = inode, .path = path, - .archive = archive, + .tables = tables, .options = options, .parent_finish = parent_finish, .out_err = out_err, @@ -89,7 +92,7 @@ pub fn finish(self: *InodeFinish) void { return; }; defer self.fil.?.close(); - self.inode.setMetadata(self.alloc, self.archive, self.fil.?, self.options) catch |err| { + self.inode.setMetadata(self.alloc, self.tables, self.fil.?, self.options) catch |err| { if (self.options.verbose) self.options.verbose_writer.?.print("Error setting metadata to {s}: {}\n", .{ self.path, err }) catch {}; self.out_err.* = err; diff --git a/src/xattr.zig b/src/xattr.zig index 1bffd00..e5203b7 100644 --- a/src/xattr.zig +++ b/src/xattr.zig @@ -1,7 +1,7 @@ const std = @import("std"); const DecompFn = @import("decomp.zig").DecompFn; -const Table = @import("table.zig").Table; +const Table = @import("tables.zig").Table; const MetadataReader = @import("util/metadata.zig"); const OffsetFile = @import("util/offset_file.zig"); @@ -30,7 +30,7 @@ const KeyRaw = packed struct { }; pub const KeyValue = struct { - key: []u8, + key: [:0]u8, value: []u8, }; @@ -62,7 +62,7 @@ pub fn init(alloc: std.mem.Allocator, fil: OffsetFile, decomp: DecompFn, table_s .table = try .init(alloc, fil, decomp, table_start + 16, info.count), }; } -pub fn deinit(self: XattrTable) void { +pub fn deinit(self: *XattrTable) void { self.table.deinit(); } @@ -80,19 +80,22 @@ pub fn get(self: *XattrTable, alloc: std.mem.Allocator, idx: u32) ![]KeyValue { switch (key_raw.type.prefix) { .user => { - kv.key = try alloc.alloc(u8, key_raw.name_size + 5); + kv.key = @ptrCast(try alloc.alloc(u8, key_raw.name_size + 5 + 1)); @memcpy(kv.key[0..5], "user."); - try meta.interface.readSliceAll(kv.key[5..]); + try meta.interface.readSliceAll(kv.key[5 .. kv.key.len - 1]); + kv.key[kv.key.len - 1] = 0; }, .security => { - kv.key = try alloc.alloc(u8, key_raw.name_size + 9); + kv.key = @ptrCast(try alloc.alloc(u8, key_raw.name_size + 9 + 1)); @memcpy(kv.key[0..9], "security."); - try meta.interface.readSliceAll(kv.key[9..]); + try meta.interface.readSliceAll(kv.key[9 .. kv.key.len - 1]); + kv.key[kv.key.len - 1] = 0; }, .trusted => { - kv.key = try alloc.alloc(u8, key_raw.name_size + 8); + kv.key = @ptrCast(try alloc.alloc(u8, key_raw.name_size + 8 + 1)); @memcpy(kv.key[0..8], "trusted."); - try meta.interface.readSliceAll(kv.key[8..]); + try meta.interface.readSliceAll(kv.key[8 .. kv.key.len - 1]); + kv.key[kv.key.len - 1] = 0; }, } if (key_raw.type.out_of_line) {