Xattr table lookup

Finished Symlink & mknod inodes
This commit is contained in:
Caleb Gardner
2026-05-31 05:57:10 -05:00
parent c52fb15609
commit 01d87f6948
3 changed files with 243 additions and 19 deletions
+122 -12
View File
@@ -37,14 +37,11 @@ pub fn extract(alloc: std.mem.Allocator, io: Io, inode: Inode, cache: *DecompCac
var frag_table: Lookup.Table(Lookup.FragmentEntry) = .init(alloc, cache, super.frag_start, super.frag_count);
defer frag_table.deinit();
var ret_loop = io.async(returnLoop, .{ alloc, &sel, options });
var ret_loop = io.async(returnLoop, .{ alloc, io, &sel, options });
try extractReal(alloc, io, cache, super, &sel, &frag_table, path, inode, null, false);
ret_loop.await(io) catch |err| {
// TODO: Drain sel
return err;
};
try ret_loop.await(io);
}
fn extractReal(
@@ -59,7 +56,14 @@ fn extractReal(
parent: ?*Atomic(usize),
origin: bool,
) Error!void {
try io.checkCancel();
io.checkCancel() catch |err| {
if (parent != null) _ = parent.?.fetchSub(1, .acquire);
if (!origin) {
alloc.free(path);
inode.deinit(alloc);
}
return err;
};
switch (inode.data) {
.dir, .ext_dir => sel.async(
@@ -72,7 +76,16 @@ fn extractReal(
extractFile,
.{ alloc, io, cache, super.block_size, frag_table, path, inode, parent, origin },
),
else => return error.Canceled,
.symlink, .ext_symlink => sel.async(
.void_ret,
extractSymlink,
.{ alloc, io, path, inode, parent, origin },
),
else => sel.async(
.file_ret,
extractNod,
.{ alloc, path, inode, parent, origin },
),
}
}
@@ -209,10 +222,96 @@ fn extractFile(
return ret;
}
fn extractSymlink(alloc: std.mem.Allocator, io: Io, path: []const u8, inode: Inode, parent: ?*Atomic(usize), origin: bool) Error!void {
defer {
if (parent != null)
_ = parent.?.fetchSub(1, .acquire);
if (!origin) {
inode.deinit(alloc);
alloc.free(path);
}
}
const target = switch (inode.data) {
.symlink => |s| s.target,
.ext_symlink => |s| s.target,
else => unreachable,
};
try Io.Dir.cwd().symLink(io, target, path, .{});
}
fn extractNod(alloc: std.mem.Allocator, path: []const u8, inode: Inode, parent: ?*Atomic(usize), origin: bool) Error!FileReturn {
defer {
if (parent != null)
_ = parent.?.fetchSub(1, .acquire);
if (!origin) inode.deinit(alloc);
}
errdefer if (!origin) alloc.free(path);
var ret: FileReturn = .{
.path = path,
.origin = origin,
.uid_idx = inode.hdr.uid_idx,
.gid_idx = inode.hdr.gid_idx,
.permissions = inode.hdr.permission,
.mod_time = inode.hdr.mod_time,
};
const DT = std.os.linux.DT;
var dev: u32 = 0;
var mode: u32 = undefined;
switch (inode.data) {
.char_dev => |d| {
dev = d.device;
mode = DT.CHR;
},
.ext_char_dev => |d| {
dev = d.device;
mode = DT.CHR;
if (d.xattr_idx != 0xFFFFFFFF)
ret.xattr_idx = d.xattr_idx;
},
.block_dev => |d| {
dev = d.device;
mode = DT.BLK;
},
.ext_block_dev => |d| {
dev = d.device;
mode = DT.BLK;
if (d.xattr_idx != 0xFFFFFFFF)
ret.xattr_idx = d.xattr_idx;
},
.fifo => mode = DT.FIFO,
.ext_fifo => |f| {
mode = DT.FIFO;
if (f.xattr_idx != 0xFFFFFFFF)
ret.xattr_idx = f.xattr_idx;
},
.socket => mode = DT.SOCK,
.ext_socket => |s| {
mode = DT.SOCK;
if (s.xattr_idx != 0xFFFFFFFF)
ret.xattr_idx = s.xattr_idx;
},
else => unreachable,
}
const sentinel_path = try std.mem.concatWithSentinel(alloc, u8, &.{path}, 0);
defer alloc.free(sentinel_path);
const res = std.os.linux.mknod(sentinel_path, mode, dev);
if (res != 0)
return Error.MknodError;
return ret;
}
// Loop
fn returnLoop(alloc: std.mem.Allocator, sel: *Io.Select(ReturnUnion), options: ExtractionOptions) !void {
fn returnLoop(alloc: std.mem.Allocator, io: Io, id_table: Lookup.Table(u16), xattr_table: Lookup.Table(Lookup.XattrEntry), sel: *Io.Select(ReturnUnion), options: ExtractionOptions) !void {
while (true) {
const finished = try sel.await();
@@ -220,7 +319,7 @@ fn returnLoop(alloc: std.mem.Allocator, sel: *Io.Select(ReturnUnion), options: E
.dir_ret => |d| {
const ret = try d;
if (ret.sub_files.load(.unordered) != 0) {
sel.queue.putOne(sel.io, .{ .dir_ret = ret }) catch |err| {
sel.queue.putOne(io, .{ .dir_ret = ret }) catch |err| {
if (!ret.origin) alloc.free(ret.path);
return err;
};
@@ -229,8 +328,18 @@ fn returnLoop(alloc: std.mem.Allocator, sel: *Io.Select(ReturnUnion), options: E
if (!ret.origin) alloc.free(ret.path);
alloc.destroy(ret.sub_files);
if (!options.ignore_permissions and !options.ignore_xattr) {
// TODO: set permissions & xattr.
if (!options.ignore_permissions and (!options.ignore_xattr and ret.xattr_idx != null)) {
const file = try Io.Dir.cwd().openFile(io, ret.path, .{});
defer file.close(io);
if (!options.ignore_permissions) {
try file.setTimestamps(io, .{
.modify_timestamp = .init(.{ .nanoseconds = @as(i96, @intCast(ret.mod_time)) * std.time.ns_per_s }),
});
try file.setPermissions(io, @enumFromInt(ret.permissions));
try file.setOwner(io, try id_table.get(io, ret.uid_idx), try id_table.get(io, ret.gid_idx));
}
if (!options.ignore_xattr and ret.xattr_idx != null) {}
}
},
.file_ret => |f| {
@@ -256,7 +365,8 @@ const ReturnUnion = union(enum) {
void_ret: Error!void,
};
const Error = error{Canceled} || Directory.Error || Io.Dir.CreateFileAtomicError || Io.File.Atomic.LinkError || DataExtractor.Error;
const Error = error{ Canceled, MknodError } || Directory.Error || Io.Dir.CreateFileAtomicError || Io.File.Atomic.LinkError ||
DataExtractor.Error || Io.Dir.SymLinkError;
const FileReturn = struct {
path: []const u8,
-7
View File
@@ -2,7 +2,6 @@ const std = @import("std");
const Io = std.Io;
const DataBlock = @import("inode.zig").DataBlock;
const InodeRef = @import("inode.zig").Ref;
const DecompCache = @import("decomp_cache.zig");
const MetadataReader = @import("meta_rdr.zig");
@@ -106,9 +105,3 @@ pub const FragmentEntry = extern struct {
size: DataBlock,
_: u32,
};
pub const XattrEntry = extern struct {
ref: InodeRef,
count: u32,
size: u32,
};
+121
View File
@@ -0,0 +1,121 @@
const std = @import("std");
const Io = std.Io;
const DecompCache = @import("decomp_cache.zig");
const InodeRef = @import("inode.zig").Ref;
const MetadataReader = @import("meta_rdr.zig");
const XattrEntryTable = @import("lookup.zig").Table(XattrEntry);
const XattrTable = @This();
table_start: u64,
table: XattrEntryTable,
pub fn init(alloc: std.mem.Allocator, cache: *DecompCache, xattr_start: u64) !XattrTable {
const table_start = std.mem.readInt(u64, cache.map.memory[xattr_start..][0..8], .little);
const num = std.mem.readInt(u32, cache.map.memory[xattr_start + 8 ..][0..4], .little);
return .{
.table_start = table_start,
.table = .init(alloc, cache, xattr_start + 16, num),
};
}
pub fn get(self: XattrTable, alloc: std.mem.Allocator, io: Io, idx: u32) !XattrKVs {
const entry = try self.table.get(io, idx);
var meta: MetadataReader = .init(io, self.table.cache, self.table_start + entry.ref.block_start);
defer meta.deinit(io);
try meta.interface.discardAll(entry.ref.block_offset);
const xattrs = try alloc.alloc(Xattr, entry.count);
errdefer alloc.free(xattrs);
for (xattrs) |*x| {
var key: KeyEntry = undefined;
try meta.interface.readSliceEndian(KeyEntry, @ptrCast(&key), .little);
key.name_size += switch (key.type.prefix) {
.user => 5,
.trusted => 8,
.security => 9,
};
x.key = try alloc.allocSentinel(u8, key.name_size, 0);
errdefer alloc.free(x.key);
switch (key.type.prefix) {
.user => @memcpy(x.key[0..5], "user."),
.trusted => @memcpy(x.key[0..8], "trusted."),
.security => @memcpy(x.key[0..9], "security."),
}
if (!key.type.out_of_line) {
var size: u32 = undefined;
try meta.interface.readSliceEndian(u32, @ptrCast(&size), .little);
x.value = try alloc.alloc(u8, size);
errdefer alloc.free(x.value);
try meta.interface.readSliceEndian(u8, x.value, .little);
continue;
}
try meta.interface.discardAll(4);
var val_ref: InodeRef = undefined;
try meta.interface.readSliceEndian(InodeRef, @ptrCast(&val_ref), .little);
var val_meta: MetadataReader = .init(io, self.table.cache, self.table_start + val_ref.block_start);
defer val_meta.deinit(io);
try val_meta.interface.discardAll(val_ref.block_offset);
var size: u32 = undefined;
try val_meta.interface.readSliceEndian(u32, @ptrCast(&size), .little);
x.value = try alloc.alloc(u8, size);
errdefer alloc.free(x.value);
try val_meta.interface.readSliceEndian(u8, x.value, .little);
}
return .{ .xattrs = xattrs };
}
// Types
pub const XattrKVs = struct {
xattrs: []Xattr,
pub fn deinit(self: XattrKVs, alloc: std.mem.Allocator) void {
for (self.xattrs) |kv|
kv.deinit(alloc);
alloc.free(self.xattrs);
}
};
pub const Xattr = struct {
key: [:0]u8,
value: []u8,
pub fn deinit(self: Xattr, alloc: std.mem.Allocator) void {
alloc.free(self.key);
alloc.free(self.value);
}
};
const KeyEntry = extern struct {
type: packed struct(u16) {
prefix: enum(u8) {
user,
trusted,
security,
},
out_of_line: bool,
_: u7,
},
name_size: u16,
};
const XattrEntry = extern struct {
ref: InodeRef,
count: u32,
size: u32,
};