285 lines
9.2 KiB
Zig
285 lines
9.2 KiB
Zig
const std = @import("std");
|
|
const Io = std.Io;
|
|
|
|
const InodeRef = @import("inode.zig").Ref;
|
|
const LookupTable = @import("lookup_table.zig");
|
|
const Decompressor = @import("util/decompressor.zig");
|
|
const MetadataReader = @import("util/metadata.zig");
|
|
const OffsetFile = @import("util/offset_file.zig");
|
|
|
|
const XattrCachedTable = @This();
|
|
|
|
alloc: std.mem.Allocator,
|
|
|
|
fil: OffsetFile,
|
|
decomp: *const Decompressor,
|
|
|
|
kv_start: u64,
|
|
|
|
table: LookupTable.CachedTable(TableValue),
|
|
value_cache: std.AutoHashMap(InodeRef, []const u8),
|
|
value_mut: Io.Mutex,
|
|
|
|
pub fn init(alloc: std.mem.Allocator, io: Io, fil: OffsetFile, decomp: *const Decompressor, xattr_start: u64) !XattrCachedTable {
|
|
var rdr = try fil.readerAt(io, xattr_start, &[0]u8{});
|
|
|
|
var start: u64 = undefined;
|
|
try rdr.interface.readSliceEndian(u64, @ptrCast(&start), .little);
|
|
var num: u32 = undefined;
|
|
try rdr.interface.readSliceEndian(u32, @ptrCast(&num), .little);
|
|
|
|
return .{
|
|
.alloc = alloc,
|
|
|
|
.fil = fil,
|
|
.decomp = decomp,
|
|
|
|
.kv_start = start,
|
|
|
|
.table = .init(alloc, fil, xattr_start + 16, num),
|
|
.value_cache = .init(alloc),
|
|
};
|
|
}
|
|
pub fn deinit(self: *XattrCachedTable, io: Io) void {
|
|
self.table.deinit(io);
|
|
self.value_cache.deinit();
|
|
}
|
|
|
|
pub fn get(self: *XattrCachedTable, alloc: std.mem.Allocator, io: Io, idx: u32) ![]XattrSemiOwned {
|
|
const lookup = try self.table.get(io, idx);
|
|
|
|
var rdr = try self.fil.readerAt(io, self.kv_start + lookup.ref.block_start, &[0]u8{});
|
|
var meta: MetadataReader = .init(alloc, &rdr.interface, self.decomp);
|
|
try meta.interface.discardAll(lookup.ref.block_offset);
|
|
|
|
const out = try alloc.alloc(XattrSemiOwned, lookup.count);
|
|
errdefer alloc.free(out);
|
|
|
|
for (0..lookup.count) |i| {
|
|
const key_entry: KeyEntry = undefined;
|
|
try meta.interface.readSliceEndian(KeyEntry, @ptrCast(&key_entry), .little);
|
|
|
|
const key = switch (key_entry.type.namespace) {
|
|
.user => blk: {
|
|
const tmp = try alloc.alloc(u8, key_entry.name_size + 1 + 5);
|
|
errdefer alloc.free(tmp);
|
|
try meta.interface.readSliceEndian(u8, tmp[5 .. tmp.len - 1], .little);
|
|
@memset(tmp[0..5], "user.");
|
|
break :blk tmp;
|
|
},
|
|
.trusted => blk: {
|
|
const tmp = try alloc.alloc(u8, key_entry.name_size + 1 + 8);
|
|
errdefer alloc.free(tmp);
|
|
try meta.interface.readSliceEndian(u8, tmp[8 .. tmp.len - 1], .little);
|
|
@memset(tmp[0..8], "trusted.");
|
|
break :blk tmp;
|
|
},
|
|
.security => blk: {
|
|
const tmp = try alloc.alloc(u8, key_entry.name_size + 1 + 9);
|
|
errdefer alloc.free(tmp);
|
|
try meta.interface.readSliceEndian(u8, tmp[9 .. tmp.len - 1], .little);
|
|
@memset(tmp[0..9], "security.");
|
|
break :blk tmp;
|
|
},
|
|
};
|
|
key[key.len - 1] = 0;
|
|
errdefer alloc.free(key);
|
|
|
|
if (key_entry.type.out_of_line) {
|
|
const value: ValueOutOfLineEntry = undefined;
|
|
try meta.interface.readSliceEndian(ValueOutOfLineEntry, @ptrCast(&value), .little);
|
|
|
|
out[i] = .{
|
|
.key = key,
|
|
.value = try self.valueAt(io, value.ref),
|
|
};
|
|
continue;
|
|
}
|
|
const val_ref: InodeRef = .{ .block_start = meta.cur_block_start, .block_offset = meta.interface.seek };
|
|
|
|
try self.value_mut.lock(io);
|
|
defer self.value_mut.unlock(io);
|
|
if (self.value_cache.contains(val_ref)) {
|
|
out[i] = .{
|
|
.key = key,
|
|
.value = try self.valueAt(io, val_ref),
|
|
};
|
|
continue;
|
|
}
|
|
|
|
var val_size: u32 = undefined;
|
|
try meta.interface.readSliceEndian(val_size, @ptrCast(&val_size), .little);
|
|
|
|
const val = try self.alloc.alloc(u8, val_size);
|
|
errdefer alloc.free(val);
|
|
try meta.interface.readSliceEndian(u8, val, .little);
|
|
|
|
try self.value_cache.put(val_ref, val);
|
|
out[i] = .{
|
|
.key = key,
|
|
.value = val,
|
|
};
|
|
}
|
|
return out;
|
|
}
|
|
|
|
fn valueAt(self: *XattrCachedTable, io: Io, ref: InodeRef) ![]const u8 {
|
|
try self.value_mut.lock(io);
|
|
defer self.value_mut.unlock(io);
|
|
|
|
if (self.value_cache.contains(ref)) return self.value_cache.get(ref).?;
|
|
|
|
var rdr = try self.fil.readerAt(io, self.kv_start + ref.block_start, &[0]u8{});
|
|
var meta: MetadataReader = .init(self.alloc, &rdr.interface, self.decomp);
|
|
try meta.interface.discardAll(ref.block_offset);
|
|
|
|
var val_size: u32 = undefined;
|
|
try meta.interface.readSliceEndian(val_size, @ptrCast(&val_size), .little);
|
|
|
|
const val = try self.alloc.alloc(u8, val_size);
|
|
errdefer self.alloc.free(val);
|
|
try meta.interface.readSliceEndian(u8, val, .little);
|
|
|
|
try self.value_cache.put(ref, val);
|
|
return val;
|
|
}
|
|
|
|
// Types
|
|
|
|
/// An Xattr return value where the reciever only owns the key value.
|
|
pub const XattrSemiOwned = struct {
|
|
key: [:0]const u8,
|
|
value: []const u8,
|
|
|
|
pub fn deinit(self: XattrSemiOwned, alloc: std.mem.Allocator) void {
|
|
alloc.free(self.key);
|
|
}
|
|
};
|
|
/// An Xattr return value where the reciever owns both the key & value.
|
|
pub const XattrOwned = struct {
|
|
key: [:0]const u8,
|
|
value: []const u8,
|
|
|
|
pub fn deinit(self: XattrSemiOwned, alloc: std.mem.Allocator) void {
|
|
alloc.free(self.key);
|
|
alloc.free(self.value);
|
|
}
|
|
};
|
|
|
|
const TableValue = extern struct {
|
|
ref: InodeRef,
|
|
count: u32,
|
|
size: u32,
|
|
};
|
|
|
|
const KeyEntry = extern struct {
|
|
type: XattrPrefix,
|
|
name_size: u16,
|
|
};
|
|
const ValueOutOfLineEntry = extern struct {
|
|
_: u32,
|
|
ref: InodeRef,
|
|
};
|
|
|
|
const XattrPrefix = packed struct(u16) {
|
|
namespace: enum(u8) {
|
|
user,
|
|
trusted,
|
|
security,
|
|
|
|
fn prefixSize(self: @This()) u16 {
|
|
return switch (self) {
|
|
.user => 5,
|
|
.trusted => 8,
|
|
.security => 9,
|
|
};
|
|
}
|
|
},
|
|
out_of_line: bool,
|
|
_: u7,
|
|
};
|
|
|
|
// Stateless
|
|
|
|
pub fn statelessLookup(alloc: std.mem.Allocator, io: Io, decomp: *const Decompressor, fil: OffsetFile, table_start: u64, idx: u16) ![]XattrOwned {
|
|
var rdr = try fil.readerAt(io, table_start, &[0]u8{});
|
|
|
|
var kv_start: u64 = undefined;
|
|
try rdr.interface.readSliceEndian(u64, @ptrCast(&kv_start), .little);
|
|
|
|
const lookup = try LookupTable.lookupValue(TableValue, alloc, io, decomp, fil, table_start + 16, idx);
|
|
|
|
rdr = try fil.readerAt(io, kv_start + lookup.ref.block_start, &[0]u8{});
|
|
var meta: MetadataReader = .init(alloc, &rdr.interface, decomp);
|
|
try meta.interface.discardAll(lookup.ref.block_offset);
|
|
|
|
const out = try alloc.alloc(XattrOwned, lookup.count);
|
|
errdefer alloc.free(out);
|
|
|
|
for (0..lookup.count) |i| {
|
|
const key_entry: KeyEntry = undefined;
|
|
try meta.interface.readSliceEndian(KeyEntry, @ptrCast(&key_entry), .little);
|
|
|
|
const key = switch (key_entry.type.namespace) {
|
|
.user => blk: {
|
|
const tmp = try alloc.alloc(u8, key_entry.name_size + 1 + 5);
|
|
errdefer alloc.free(tmp);
|
|
try meta.interface.readSliceEndian(u8, tmp[5 .. tmp.len - 1], .little);
|
|
@memset(tmp[0..5], "user.");
|
|
break :blk tmp;
|
|
},
|
|
.trusted => blk: {
|
|
const tmp = try alloc.alloc(u8, key_entry.name_size + 1 + 8);
|
|
errdefer alloc.free(tmp);
|
|
try meta.interface.readSliceEndian(u8, tmp[8 .. tmp.len - 1], .little);
|
|
@memset(tmp[0..8], "trusted.");
|
|
break :blk tmp;
|
|
},
|
|
.security => blk: {
|
|
const tmp = try alloc.alloc(u8, key_entry.name_size + 1 + 9);
|
|
errdefer alloc.free(tmp);
|
|
try meta.interface.readSliceEndian(u8, tmp[9 .. tmp.len - 1], .little);
|
|
@memset(tmp[0..9], "security.");
|
|
break :blk tmp;
|
|
},
|
|
};
|
|
key[key.len - 1] = 0;
|
|
errdefer alloc.free(key);
|
|
|
|
if (key_entry.type.out_of_line) {
|
|
const value: ValueOutOfLineEntry = undefined;
|
|
try meta.interface.readSliceEndian(ValueOutOfLineEntry, @ptrCast(&value), .little);
|
|
|
|
var ool_rdr = try fil.readerAt(io, kv_start + value.ref.block_start, &[0]u8{});
|
|
var ool_meta: MetadataReader = .init(alloc, &ool_rdr.interface, decomp);
|
|
try ool_meta.interface.discardAll(value.ref.block_offset);
|
|
|
|
var val_size: u32 = undefined;
|
|
try ool_meta.interface.readSliceEndian(val_size, @ptrCast(&val_size), .little);
|
|
|
|
const val = try alloc.alloc(u8, val_size);
|
|
errdefer alloc.free(val);
|
|
try ool_meta.interface.readSliceEndian(u8, val, .little);
|
|
|
|
out[i] = .{
|
|
.key = key,
|
|
.value = val,
|
|
};
|
|
continue;
|
|
}
|
|
|
|
var val_size: u32 = undefined;
|
|
try meta.interface.readSliceEndian(val_size, @ptrCast(&val_size), .little);
|
|
|
|
const val = try alloc.alloc(u8, val_size);
|
|
errdefer alloc.free(val);
|
|
try meta.interface.readSliceEndian(u8, val, .little);
|
|
|
|
out[i] = .{
|
|
.key = key,
|
|
.value = val,
|
|
};
|
|
}
|
|
return out;
|
|
}
|