Added xattr support (currently untested)
Use stack fallback allocator on extraction for better performance.
This commit is contained in:
+6
-6
@@ -15,6 +15,7 @@ const Superblock = @import("super.zig").Superblock;
|
|||||||
const Table = @import("table.zig").Table;
|
const Table = @import("table.zig").Table;
|
||||||
const MetadataReader = @import("util/metadata.zig");
|
const MetadataReader = @import("util/metadata.zig");
|
||||||
const OffsetFile = @import("util/offset_file.zig");
|
const OffsetFile = @import("util/offset_file.zig");
|
||||||
|
const XattrTable = @import("xattr.zig");
|
||||||
|
|
||||||
const config = if (builtin.is_test) .{
|
const config = if (builtin.is_test) .{
|
||||||
.use_c_libs = true,
|
.use_c_libs = true,
|
||||||
@@ -38,9 +39,10 @@ decomp: Decomp.DecompFn,
|
|||||||
|
|
||||||
super: Superblock,
|
super: Superblock,
|
||||||
|
|
||||||
frag_table: Table(FragEntry) = undefined,
|
frag_table: Table(FragEntry),
|
||||||
id_table: Table(u16) = undefined,
|
id_table: Table(u16),
|
||||||
export_table: Table(InodeRef) = undefined,
|
export_table: Table(InodeRef),
|
||||||
|
xattr_table: XattrTable,
|
||||||
|
|
||||||
/// Default settings using std.Thread.getCpuCount() threads and the minimum of 4gb or half of system memory for memory usage.
|
/// 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 {
|
pub fn init(alloc: std.mem.Allocator, fil: File, offset: u64) !Archive {
|
||||||
@@ -59,15 +61,13 @@ pub fn init(alloc: std.mem.Allocator, fil: File, offset: u64) !Archive {
|
|||||||
};
|
};
|
||||||
return .{
|
return .{
|
||||||
.alloc = alloc,
|
.alloc = alloc,
|
||||||
|
|
||||||
.fil = off_fil,
|
.fil = off_fil,
|
||||||
.decomp = decomp,
|
.decomp = decomp,
|
||||||
|
|
||||||
.super = super,
|
.super = super,
|
||||||
|
|
||||||
.frag_table = try .init(alloc, off_fil, decomp, super.frag_start, super.frag_count),
|
.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),
|
.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),
|
.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 {
|
pub fn deinit(self: *Archive) void {
|
||||||
|
|||||||
+69
-71
@@ -15,6 +15,7 @@ const misc = @import("inode_data/misc.zig");
|
|||||||
const DataReader = @import("util/data.zig");
|
const DataReader = @import("util/data.zig");
|
||||||
const ThreadedDataReader = @import("util/data_threaded.zig");
|
const ThreadedDataReader = @import("util/data_threaded.zig");
|
||||||
const MetadataReader = @import("util/metadata.zig");
|
const MetadataReader = @import("util/metadata.zig");
|
||||||
|
const XattrTable = @import("xattr.zig");
|
||||||
|
|
||||||
pub const Ref = packed struct {
|
pub const Ref = packed struct {
|
||||||
block_offset: u16,
|
block_offset: u16,
|
||||||
@@ -154,7 +155,46 @@ fn entriesFromData(alloc: std.mem.Allocator, archive: Archive, data: anytype) ![
|
|||||||
return DirEntry.readDir(alloc, &meta.interface, data.size);
|
return DirEntry.readDir(alloc, &meta.interface, data.size);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Extract the inode to the given path. Single threaded.
|
/// Returns the xattr index for the given inode. If the inode isn't an extended variant or doesn't have any, the returned value is the max u32 value (0xFFFFFFFF)
|
||||||
|
pub fn xattrIdx(self: Inode) u32 {
|
||||||
|
return switch (self.data) {
|
||||||
|
.ext_dir => |d| d.xattr_id,
|
||||||
|
.ext_file => |f| f.xattr_idx,
|
||||||
|
.ext_symlink => |s| s.xattr_idx,
|
||||||
|
.ext_block_dev, .ext_char_dev => |d| d.xattr_idx,
|
||||||
|
.ext_fifo, .ext_socket => |i| i.xattr_idx,
|
||||||
|
else => 0xFFFFFFFF,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fn setPermissionAndXattr(self: Inode, alloc: std.mem.Allocator, archive: *Archive, 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));
|
||||||
|
}
|
||||||
|
if (!options.ignore_xattr) {
|
||||||
|
const idx = self.xattrIdx();
|
||||||
|
if (idx == 0xFFFFFFFF) return;
|
||||||
|
const xattrs = try archive.xattr_table.get(alloc, idx);
|
||||||
|
defer alloc.free(xattrs);
|
||||||
|
for (xattrs) |kv| {
|
||||||
|
defer {
|
||||||
|
alloc.free(kv.key);
|
||||||
|
alloc.free(kv.value);
|
||||||
|
}
|
||||||
|
const res = std.os.linux.fsetxattr(fil.handle, @ptrCast(kv.key), @ptrCast(kv.value), kv.value.len, 0);
|
||||||
|
if (res != 0) {
|
||||||
|
if (options.verbose)
|
||||||
|
options.verbose_writer.?.print("fsetxattr has result of: {}\n", .{res}) catch {};
|
||||||
|
return error.SetXattr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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 {
|
||||||
if (options.threads > 1) return self.extractToThreaded(alloc, archive, path, options);
|
if (options.threads > 1) return self.extractToThreaded(alloc, archive, path, options);
|
||||||
switch (self.hdr.inode_type) {
|
switch (self.hdr.inode_type) {
|
||||||
@@ -180,42 +220,38 @@ pub fn extractTo(self: Inode, alloc: std.mem.Allocator, archive: *Archive, path:
|
|||||||
defer inode.deinit(alloc);
|
defer inode.deinit(alloc);
|
||||||
try inode.extractTo(alloc, archive, new_path, options);
|
try inode.extractTo(alloc, archive, new_path, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var fil = try std.fs.cwd().openFile(path, .{});
|
||||||
|
defer fil.close();
|
||||||
|
try self.setPermissionAndXattr(alloc, archive, fil, options);
|
||||||
},
|
},
|
||||||
.file, .ext_file => try self.extractRegFile(alloc, archive, path, options),
|
.file, .ext_file => try self.extractRegFile(alloc, archive, path, options),
|
||||||
.symlink, .ext_symlink => try self.extractSymlink(path),
|
.symlink, .ext_symlink => try self.extractSymlink(path),
|
||||||
else => try self.extractDevice(archive, path, options),
|
else => try self.extractDevice(alloc, archive, path, options),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const Parent = struct {
|
const Parent = struct {
|
||||||
alloc: std.mem.Allocator,
|
alloc: std.mem.Allocator,
|
||||||
|
|
||||||
|
inode: Inode,
|
||||||
path: []const u8,
|
path: []const u8,
|
||||||
uid: u16,
|
archive: *Archive,
|
||||||
gid: u16,
|
options: ExtractionOptions,
|
||||||
perm: u16,
|
|
||||||
mod_time: u32,
|
|
||||||
|
|
||||||
ignore_permissions: bool,
|
|
||||||
ignore_xattr: bool,
|
|
||||||
|
|
||||||
wg: WaitGroup = .{},
|
wg: WaitGroup = .{},
|
||||||
mut: Mutex = .{},
|
mut: Mutex = .{},
|
||||||
|
|
||||||
fn create(alloc: std.mem.Allocator, hdr: Header, archive: *Archive, path: []const u8, options: ExtractionOptions, dir_size: usize) !*Parent {
|
fn create(alloc: std.mem.Allocator, inode: Inode, path: []const u8, archive: *Archive, options: ExtractionOptions, dir_size: usize) !*Parent {
|
||||||
const out = try alloc.create(Parent);
|
const out = try alloc.create(Parent);
|
||||||
errdefer alloc.destroy(out);
|
errdefer alloc.destroy(out);
|
||||||
out.* = .{
|
out.* = .{
|
||||||
.alloc = alloc,
|
.alloc = alloc,
|
||||||
|
|
||||||
|
.inode = inode,
|
||||||
.path = path,
|
.path = path,
|
||||||
.uid = try archive.id_table.get(hdr.uid_idx),
|
.archive = archive,
|
||||||
.gid = try archive.id_table.get(hdr.gid_idx),
|
.options = options,
|
||||||
.perm = hdr.permissions,
|
|
||||||
.mod_time = hdr.mod_time,
|
|
||||||
|
|
||||||
.ignore_permissions = options.ignore_permissions,
|
|
||||||
.ignore_xattr = options.ignore_xattr,
|
|
||||||
};
|
};
|
||||||
out.wg.startMany(dir_size);
|
out.wg.startMany(dir_size);
|
||||||
return out;
|
return out;
|
||||||
@@ -231,33 +267,21 @@ const Parent = struct {
|
|||||||
defer p.alloc.destroy(p);
|
defer p.alloc.destroy(p);
|
||||||
var fil = try std.fs.cwd().openFile(p.path, .{});
|
var fil = try std.fs.cwd().openFile(p.path, .{});
|
||||||
defer fil.close();
|
defer fil.close();
|
||||||
const time = @as(i128, p.mod_time) * 1000000000;
|
try p.inode.setPermissionAndXattr(p.alloc, p.archive, fil, p.options);
|
||||||
try fil.updateTimes(time, time);
|
|
||||||
if (p.ignore_permissions) {
|
|
||||||
try fil.chmod(p.perm);
|
|
||||||
try fil.chown(p.uid, p.gid);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Extract the inode to the given path. Multi-threaded.
|
/// Extract the inode to the given path. Multi-threaded.
|
||||||
/// Functions identically to extractTo on all but regular files and directories.
|
/// Functions identically to extractTo on all but regular files and directories.
|
||||||
///
|
|
||||||
/// If threads <= 1, then this just calls extractTo.
|
|
||||||
fn extractToThreaded(self: Inode, allocator: std.mem.Allocator, archive: *Archive, path: []const u8, options: ExtractionOptions) !void {
|
fn extractToThreaded(self: Inode, allocator: std.mem.Allocator, archive: *Archive, path: []const u8, options: ExtractionOptions) !void {
|
||||||
switch (self.hdr.inode_type) {
|
switch (self.hdr.inode_type) {
|
||||||
.dir, .ext_dir => {
|
.dir, .ext_dir => {
|
||||||
// Removing any trailing separators since that's the easiest path forward.
|
// Removing any trailing separators since that's the easiest path forward.
|
||||||
if (path[path.len - 1] == '/') return self.extractToThreaded(allocator, archive, path[0 .. path.len - 1], options);
|
if (path[path.len - 1] == '/') return self.extractToThreaded(allocator, archive, path[0 .. path.len - 1], options);
|
||||||
|
|
||||||
// Fixed Allocator
|
|
||||||
// const mem_buf = archive.allocator().alloc(u8, 2 * 1024 * 1024 * 1024);
|
|
||||||
// defer archive.allocator().free(mem_buf);
|
|
||||||
// var fixed_alloc: std.heap.FixedBufferAllocator = .init(mem_buf);
|
|
||||||
// const alloc = fixed_alloc.threadSafeAllocator();
|
|
||||||
|
|
||||||
// Arena Allocator
|
// Arena Allocator
|
||||||
var arena_alloc: std.heap.ArenaAllocator = .init(allocator);
|
var stack_alloc = std.heap.stackFallback(1024 * 1024, allocator);
|
||||||
|
var arena_alloc: std.heap.ArenaAllocator = .init(stack_alloc.get());
|
||||||
defer arena_alloc.deinit();
|
defer arena_alloc.deinit();
|
||||||
var thread_alloc: std.heap.ThreadSafeAllocator = .{ .child_allocator = arena_alloc.allocator() };
|
var thread_alloc: std.heap.ThreadSafeAllocator = .{ .child_allocator = arena_alloc.allocator() };
|
||||||
const alloc = thread_alloc.allocator();
|
const alloc = thread_alloc.allocator();
|
||||||
@@ -265,7 +289,7 @@ fn extractToThreaded(self: Inode, allocator: std.mem.Allocator, archive: *Archiv
|
|||||||
var wg: WaitGroup = .{};
|
var wg: WaitGroup = .{};
|
||||||
// defer if(!options.ignore_permissions) perms.?.deinit(alloc); We don't need to do this due to ArenaAllocator
|
// defer if(!options.ignore_permissions) perms.?.deinit(alloc); We don't need to do this due to ArenaAllocator
|
||||||
var pool: Pool = undefined;
|
var pool: Pool = undefined;
|
||||||
try pool.init(.{ .allocator = allocator, .n_jobs = options.threads - 1 });
|
try pool.init(.{ .allocator = alloc, .n_jobs = options.threads - 1 });
|
||||||
defer pool.deinit();
|
defer pool.deinit();
|
||||||
var out_err: ?anyerror = null;
|
var out_err: ?anyerror = null;
|
||||||
|
|
||||||
@@ -276,19 +300,16 @@ fn extractToThreaded(self: Inode, allocator: std.mem.Allocator, archive: *Archiv
|
|||||||
|
|
||||||
var fil = try std.fs.cwd().openFile(path, .{});
|
var fil = try std.fs.cwd().openFile(path, .{});
|
||||||
defer fil.close();
|
defer fil.close();
|
||||||
const time = @as(i128, self.hdr.mod_time) * 1000000000;
|
try self.setPermissionAndXattr(alloc, archive, fil, options);
|
||||||
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));
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
.file, .ext_file => {
|
.file, .ext_file => {
|
||||||
var pool: Pool = undefined;
|
var pool: Pool = undefined;
|
||||||
try pool.init(.{ .allocator = allocator, .n_jobs = options.threads - 1 });
|
try pool.init(.{ .allocator = allocator, .n_jobs = options.threads - 1 });
|
||||||
defer pool.deinit();
|
defer pool.deinit();
|
||||||
|
|
||||||
var arena_alloc: std.heap.ArenaAllocator = .init(allocator);
|
// Arena Allocator
|
||||||
|
var stack_alloc = std.heap.stackFallback(1024 * 1024, allocator);
|
||||||
|
var arena_alloc: std.heap.ArenaAllocator = .init(stack_alloc.get());
|
||||||
defer arena_alloc.deinit();
|
defer arena_alloc.deinit();
|
||||||
var thread_alloc: std.heap.ThreadSafeAllocator = .{ .child_allocator = arena_alloc.allocator() };
|
var thread_alloc: std.heap.ThreadSafeAllocator = .{ .child_allocator = arena_alloc.allocator() };
|
||||||
const alloc = thread_alloc.allocator();
|
const alloc = thread_alloc.allocator();
|
||||||
@@ -297,15 +318,10 @@ fn extractToThreaded(self: Inode, allocator: std.mem.Allocator, archive: *Archiv
|
|||||||
|
|
||||||
var fil = try std.fs.cwd().openFile(path, .{});
|
var fil = try std.fs.cwd().openFile(path, .{});
|
||||||
defer fil.close();
|
defer fil.close();
|
||||||
const time = @as(i128, self.hdr.mod_time) * 1000000000;
|
try self.setPermissionAndXattr(alloc, archive, fil, options);
|
||||||
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));
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
.symlink, .ext_symlink => try self.extractSymlink(path),
|
.symlink, .ext_symlink => try self.extractSymlink(path),
|
||||||
else => try self.extractDevice(archive, path, options),
|
else => try self.extractDevice(allocator, archive, path, options),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -374,7 +390,7 @@ fn extractThread(
|
|||||||
out_err.* = err;
|
out_err.* = err;
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
const p = Parent.create(alloc, self.hdr, archive, path, options, entries.len) catch |err| {
|
const p = Parent.create(alloc, self, path, archive, options, entries.len) catch |err| {
|
||||||
out_err.* = err;
|
out_err.* = err;
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
@@ -422,7 +438,7 @@ fn extractThread(
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
else => {
|
else => {
|
||||||
self.extractDevice(archive, path, options) catch |err| {
|
self.extractDevice(alloc, archive, path, options) catch |err| {
|
||||||
if (options.verbose)
|
if (options.verbose)
|
||||||
options.verbose_writer.?.print("Error extracting device/IPC inode #{} to {s}: {}\n", .{ self.hdr.num, path, err }) catch {};
|
options.verbose_writer.?.print("Error extracting device/IPC inode #{} to {s}: {}\n", .{ self.hdr.num, path, err }) catch {};
|
||||||
out_err.* = err;
|
out_err.* = err;
|
||||||
@@ -443,12 +459,7 @@ fn extractRegFile(self: Inode, alloc: std.mem.Allocator, archive: *Archive, path
|
|||||||
_ = try dat_rdr.interface.streamRemaining(&wrt.interface);
|
_ = try dat_rdr.interface.streamRemaining(&wrt.interface);
|
||||||
try wrt.interface.flush();
|
try wrt.interface.flush();
|
||||||
|
|
||||||
const time = @as(i128, self.hdr.mod_time) * 1000000000;
|
try self.setPermissionAndXattr(alloc, archive, fil, options);
|
||||||
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));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
/// Extract the inode file contents to the given path threadedly.
|
/// Extract the inode file contents to the given path threadedly.
|
||||||
/// pool is used to spawn threads.
|
/// pool is used to spawn threads.
|
||||||
@@ -460,12 +471,7 @@ fn extractRegFileThreaded(self: Inode, alloc: std.mem.Allocator, archive: *Archi
|
|||||||
var data = try self.threadedDataReader(alloc, archive);
|
var data = try self.threadedDataReader(alloc, archive);
|
||||||
try data.extractThreaded(fil, pool);
|
try data.extractThreaded(fil, pool);
|
||||||
|
|
||||||
const time = @as(i128, self.hdr.mod_time) * 1000000000;
|
try self.setPermissionAndXattr(alloc, archive, fil, options);
|
||||||
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));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
/// Creates the symlink described by the inode.
|
/// Creates the symlink described by the inode.
|
||||||
///
|
///
|
||||||
@@ -482,7 +488,7 @@ fn extractSymlink(self: Inode, path: []const u8) !void {
|
|||||||
///
|
///
|
||||||
/// Optionally set owner & permissions.
|
/// Optionally set owner & permissions.
|
||||||
/// Assumes the inode is a char_dev, block_dev, fifo, socket, or their extended counterparts.
|
/// Assumes the inode is a char_dev, block_dev, fifo, socket, or their extended counterparts.
|
||||||
fn extractDevice(self: Inode, archive: *Archive, path: []const u8, options: ExtractionOptions) !void {
|
fn extractDevice(self: Inode, alloc: std.mem.Allocator, archive: *Archive, path: []const u8, options: ExtractionOptions) !void {
|
||||||
var mode: u32 = undefined;
|
var mode: u32 = undefined;
|
||||||
var dev: u32 = 0;
|
var dev: u32 = 0;
|
||||||
switch (self.data) {
|
switch (self.data) {
|
||||||
@@ -527,13 +533,5 @@ fn extractDevice(self: Inode, archive: *Archive, path: []const u8, options: Extr
|
|||||||
}
|
}
|
||||||
var fil = try std.fs.cwd().openFile(path, .{});
|
var fil = try std.fs.cwd().openFile(path, .{});
|
||||||
defer fil.close();
|
defer fil.close();
|
||||||
const time = @as(i128, self.hdr.mod_time) * 1000000000;
|
try self.setPermissionAndXattr(alloc, archive, fil, options);
|
||||||
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));
|
|
||||||
}
|
|
||||||
if (!options.ignore_xattr) {
|
|
||||||
// TODO
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+114
@@ -0,0 +1,114 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
const DecompFn = @import("decomp.zig").DecompFn;
|
||||||
|
const Table = @import("table.zig").Table;
|
||||||
|
const MetadataReader = @import("util/metadata.zig");
|
||||||
|
const OffsetFile = @import("util/offset_file.zig");
|
||||||
|
|
||||||
|
const Ref = packed struct {
|
||||||
|
block_offset: u16,
|
||||||
|
block_start: u32,
|
||||||
|
_: u16,
|
||||||
|
};
|
||||||
|
const Entry = packed struct {
|
||||||
|
ref: Ref,
|
||||||
|
count: u32,
|
||||||
|
size: u32,
|
||||||
|
};
|
||||||
|
const KeyPrefix = enum(u8) {
|
||||||
|
user,
|
||||||
|
trusted,
|
||||||
|
security,
|
||||||
|
};
|
||||||
|
const KeyRaw = packed struct {
|
||||||
|
type: packed struct {
|
||||||
|
prefix: KeyPrefix,
|
||||||
|
out_of_line: bool,
|
||||||
|
_: u7,
|
||||||
|
},
|
||||||
|
name_size: u16,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const KeyValue = struct {
|
||||||
|
key: []u8,
|
||||||
|
value: []u8,
|
||||||
|
};
|
||||||
|
|
||||||
|
const XattrTable = @This();
|
||||||
|
|
||||||
|
alloc: std.mem.Allocator,
|
||||||
|
fil: OffsetFile,
|
||||||
|
decomp: DecompFn,
|
||||||
|
|
||||||
|
count: u32,
|
||||||
|
start: u64,
|
||||||
|
|
||||||
|
table: Table(Entry),
|
||||||
|
|
||||||
|
pub fn init(alloc: std.mem.Allocator, fil: OffsetFile, decomp: DecompFn, table_start: u64) !XattrTable {
|
||||||
|
var info = packed struct {
|
||||||
|
start: u64 = undefined,
|
||||||
|
count: u32 = undefined,
|
||||||
|
_: u32 = undefined,
|
||||||
|
}{};
|
||||||
|
var rdr = try fil.readerAt(table_start, &[0]u8{});
|
||||||
|
try rdr.interface.readSliceEndian(@TypeOf(info), @ptrCast(&info), .little);
|
||||||
|
return .{
|
||||||
|
.alloc = alloc,
|
||||||
|
.fil = fil,
|
||||||
|
.decomp = decomp,
|
||||||
|
.count = info.count,
|
||||||
|
.start = info.start,
|
||||||
|
.table = try .init(alloc, fil, decomp, table_start + 16, info.count),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
pub fn deinit(self: XattrTable) void {
|
||||||
|
self.table.deinit();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(self: *XattrTable, alloc: std.mem.Allocator, idx: u32) ![]KeyValue {
|
||||||
|
const entry: Entry = try self.table.get(idx);
|
||||||
|
const out = try alloc.alloc(KeyValue, entry.count);
|
||||||
|
|
||||||
|
for (out) |*kv| {
|
||||||
|
var rdr = try self.fil.readerAt(self.start + entry.ref.block_start, &[0]u8{});
|
||||||
|
var meta: MetadataReader = .init(alloc, &rdr.interface, self.decomp);
|
||||||
|
try meta.interface.discardAll(entry.ref.block_offset);
|
||||||
|
|
||||||
|
var key_raw: KeyRaw = undefined;
|
||||||
|
try meta.interface.readSliceEndian(KeyRaw, @ptrCast(&key_raw), .little);
|
||||||
|
|
||||||
|
switch (key_raw.type.prefix) {
|
||||||
|
.user => {
|
||||||
|
kv.key = try alloc.alloc(u8, key_raw.name_size + 5);
|
||||||
|
@memcpy(kv.key[0..5], "user.");
|
||||||
|
try meta.interface.readSliceAll(kv.key[5..]);
|
||||||
|
},
|
||||||
|
.security => {
|
||||||
|
kv.key = try alloc.alloc(u8, key_raw.name_size + 9);
|
||||||
|
@memcpy(kv.key[0..9], "security.");
|
||||||
|
try meta.interface.readSliceAll(kv.key[9..]);
|
||||||
|
},
|
||||||
|
.trusted => {
|
||||||
|
kv.key = try alloc.alloc(u8, key_raw.name_size + 8);
|
||||||
|
@memcpy(kv.key[0..8], "trusted.");
|
||||||
|
try meta.interface.readSliceAll(kv.key[8..]);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if (key_raw.type.out_of_line) {
|
||||||
|
try meta.interface.discardAll(4);
|
||||||
|
var ref: Ref = undefined;
|
||||||
|
try meta.interface.readSliceEndian(Ref, @ptrCast(&ref), .little);
|
||||||
|
|
||||||
|
rdr = try self.fil.readerAt(self.start + ref.block_start, &[0]u8{});
|
||||||
|
meta = .init(alloc, &rdr.interface, self.decomp);
|
||||||
|
try meta.interface.discardAll(ref.block_offset);
|
||||||
|
}
|
||||||
|
var value_size: u32 = undefined;
|
||||||
|
try meta.interface.readSliceEndian(u32, @ptrCast(&value_size), .little);
|
||||||
|
kv.value = try alloc.alloc(u8, value_size);
|
||||||
|
try meta.interface.readSliceAll(kv.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user