Finished work (theoretically) on single threaded extraction

Started work on Multi-threaded extraction
Added decompression cache.
Added XattrTable
This commit is contained in:
Caleb Gardner
2026-06-13 06:06:27 -05:00
parent f3318e55a3
commit 8a9ff7bf5f
11 changed files with 664 additions and 39 deletions
+2
View File
@@ -15,6 +15,7 @@ pub fn build(b: *std.Build) !void {
const build_options = b.addOptions();
build_options.addOption(bool, "use_zig_decomp", use_zig_decomp);
build_options.addOption(bool, "allow_lzo", allow_lzo);
build_options.addOption(std.SemanticVersion, "version", version);
const c = b.addTranslateC(.{
.optimize = optimize,
@@ -79,6 +80,7 @@ pub fn build(b: *std.Build) !void {
.valgrind = debug,
.imports = &.{
.{ .name = "squashfs", .module = lib.root_module },
.{ .name = "build_options", .module = build_options.createModule() },
},
}),
});
+32 -18
View File
@@ -1,9 +1,10 @@
const std = @import("std");
const Writer = std.Io.Writer;
const Io = std.Io;
const Writer = Io.Writer;
const builtin = @import("builtin");
const build = @import("build_options");
const config = @import("config");
const squashfs = @import("zig_squashfs");
const squashfs = @import("squashfs");
//TODO: Add more options
const help_mgs =
@@ -30,7 +31,7 @@ const help_mgs =
const errors = error{InvalidArguments};
var archive: []const u8 = "";
var extLoc: []const u8 = "squashfs-root";
var ext_loc: []const u8 = "squashfs-root";
var offset: u64 = 0;
var threads: u32 = 0;
var verbose: bool = false;
@@ -38,35 +39,48 @@ var ignore_xattrs: bool = false;
var ignore_permissions: bool = false;
var force: bool = false;
pub fn main() !void {
const alloc = std.heap.smp_allocator;
var stdout = std.fs.File.stdout();
var out = stdout.writer(&[0]u8{});
pub fn main(init: std.process.Init) !void {
const io = init.io;
const alloc = init.alloc;
var stdout = Io.File.stdout();
var out = stdout.writer(io, &[0]u8{});
defer out.interface.flush() catch {};
try handleArgs(alloc, &out.interface);
try handleArgs(init.minimal.args, &out.interface);
if (archive.len == 0) {
try out.interface.print("You must provide a squashfs archive\n", .{});
try out.interface.print(help_mgs, .{});
return;
}
var fil: std.fs.File = try std.fs.cwd().openFile(archive, .{}); //TODO: Handle error gracefully.
var fil = try Io.Dir.cwd().openFile(io, archive, .{}); //TODO: Handle error gracefully.
defer fil.close();
var arc: squashfs.Archive = try .init(alloc, fil, offset); //TODO: Update when memory size matters. //TODO: Handle error gracefully.
var arc: squashfs.Archive = try .init(io, fil, offset); //TODO: Update when memory size matters. //TODO: Handle error gracefully.
defer arc.deinit();
const options: squashfs.ExtractionOptions = .{
.threads = if (threads == 0) try std.Thread.getCpuCount() else threads,
.single_threaded = threads == 1,
.verbose = verbose,
.verbose_writer = if (verbose) &out.interface else null,
.ignore_xattr = ignore_xattrs,
.ignore_permissions = ignore_permissions,
};
if (force)
try std.fs.cwd().deleteTree(extLoc);
try arc.extract(alloc, extLoc, options); //TODO: Handle error gracefully.
try std.fs.cwd().deleteTree(ext_loc);
if (threads > 1) {
var limited_io = Io.Threaded.init(alloc, .{
.argv0 = .init(init.minimal.args),
.async_limit = threads,
.concurrent_limit = threads,
.environ = init.minimal.environ,
});
try arc.extract(alloc, limited_io.io(), ext_loc, options); //TODO: Handle error gracefully.
} else {
try arc.extract(alloc, io, ext_loc, options); //TODO: Handle error gracefully.
}
}
fn handleArgs(alloc: std.mem.Allocator, out: *Writer) !void {
var args = try std.process.argsWithAllocator(alloc);
fn handleArgs(main_args: std.process.Args, out: *Writer) !void {
var args = main_args.iterate();
defer args.deinit();
_ = args.next(); // args[0] is the application launch command.
while (args.next()) |arg| {
@@ -87,7 +101,7 @@ fn handleArgs(alloc: std.mem.Allocator, out: *Writer) !void {
try out.print("-d must be followed by a location\n", .{});
return errors.InvalidArguments;
}
extLoc = nxt.?;
ext_loc = nxt.?;
continue;
} else if (std.mem.eql(u8, arg, "-p")) {
const nxt = args.next();
@@ -114,7 +128,7 @@ fn handleArgs(alloc: std.mem.Allocator, out: *Writer) !void {
continue;
} else if (std.mem.eql(u8, arg, "--version")) {
try out.print("zig-unsquashfs v", .{});
try config.version.format(out);
try build.version.format(out);
try out.print("\nBuilt using Zig {s} in {} mode\n", .{ builtin.zig_version_string, builtin.mode });
std.process.exit(0);
return;
+21 -5
View File
@@ -3,6 +3,7 @@ const Io = std.Io;
const Decomp = @import("../decomp.zig");
const DataBlock = @import("../inode.zig").DataBlock;
const Cache = @import("../util/cache.zig");
const Extractor = @This();
@@ -18,6 +19,8 @@ size: u64,
frag_data: ?[]u8 = null,
frag_offset: u32 = 0,
cache: ?Cache = null,
pub fn init(data: []u8, decomp: Decomp.Fn, block_size: u32, blocks: []DataBlock, start: u64, size: u64) Extractor {
return .{
.data = data,
@@ -34,6 +37,9 @@ pub fn addFrag(self: *Extractor, frag_data: []u8, frag_offset: u32) void {
self.frag_data = frag_data;
self.frag_offset = frag_offset;
}
pub fn addCache(self: *Extractor, cache: Cache) void {
self.cache = cache;
}
pub fn extractAsync(self: Extractor, alloc: std.mem.Allocator, io: Io, file: Io.File) Error!void {
if (self.size == 0) return;
@@ -86,10 +92,20 @@ fn blockThread(self: Extractor, alloc: std.mem.Allocator, map_data: []u8, read_o
return;
}
_ = self.decomp(alloc, data, map_data[offset..][0..size]) catch |inner_err| {
err.* = inner_err;
return;
};
if (self.cache != null) {
const decomp_block = self.cache.?.get(read_offset, block.size) catch |inner_err| {
switch (inner_err) {
error.Canceled => return error.Canceled,
else => |e| err.* = e,
}
return;
};
@memcpy(map_data[offset..][0..size], decomp_block[0..size]);
} else {
_ = self.decomp(alloc, data, map_data[offset..][0..size]) catch |inner_err| {
err.* = inner_err;
};
}
}
fn fragThread(self: Extractor, map_data: []u8) error{Canceled}!void {
const size = self.size % self.block_size;
@@ -100,4 +116,4 @@ fn fragThread(self: Extractor, map_data: []u8) error{Canceled}!void {
// Types
const Error = Io.File.WritePositionalError || Io.File.MemoryMap.CreateError || Decomp.Error;
pub const Error = Io.File.WritePositionalError || Io.File.MemoryMap.CreateError || Decomp.Error || Cache.Error;
+14 -3
View File
@@ -3,6 +3,7 @@ const Io = std.Io;
const Decomp = @import("../decomp.zig");
const DataBlock = @import("../inode.zig").DataBlock;
const Cache = @import("../util/cache.zig");
const Reader = @This();
@@ -23,6 +24,8 @@ sparse_block: bool = false,
frag_data: ?[]u8 = null,
frag_offset: u32 = 0,
cache: ?Cache = null,
block: [1024 * 1024]u8 = undefined,
interface: Io.Reader = .{
@@ -55,6 +58,9 @@ pub fn addFrag(self: *Reader, frag_data: []u8, frag_offset: u32) void {
self.frag_data = frag_data;
self.frag_offset = frag_offset;
}
pub fn addCache(self: *Reader, cache: Cache) void {
self.cache = cache;
}
fn advance(self: *Reader) Io.Reader.Error!void {
if (self.block_idx > self.blocks.len) return error.EndOfStream;
@@ -98,9 +104,14 @@ fn advance(self: *Reader) Io.Reader.Error!void {
return;
}
_ = self.decomp(self.alloc, self.data[self.offset..][0..block.size], self.block[0..size]) catch return error.ReadFailed;
self.interface.buffer = self.block[0..size];
self.interface.end = size;
if (self.cache == null) {
_ = self.decomp(self.alloc, self.data[self.offset..][0..block.size], self.block[0..size]) catch return error.ReadFailed;
self.interface.buffer = self.block[0..size];
self.interface.end = size;
} else {
self.interface.buffer = self.cache.?.get(self.io, self.offset, block.size) catch return error.ReadFailed;
self.interface.end = self.interface.buffer.len;
}
}
fn stream(r: *Io.Reader, w: *Io.Writer, limit: Io.Limit) Io.Reader.StreamError!usize {
+2
View File
@@ -58,6 +58,8 @@ pub fn deinit(self: Directory, alloc: std.mem.Allocator) void {
// Types
pub const Error = error{OutOfMemory} || Reader.Error;
pub const Entry = struct {
name: []const u8,
block_start: u32,
+198 -6
View File
@@ -1,11 +1,15 @@
const std = @import("std");
const Io = std.Io;
const DataExtractor = @import("data/extractor.zig");
const Decomp = @import("decomp.zig");
const Directory = @import("directory.zig");
const ExtractionOptions = @import("options.zig");
const Inode = @import("inode.zig");
const Lookup = @import("lookup.zig");
const MetadataReader = @import("meta_rdr.zig");
const Superblock = @import("archive.zig").Superblock;
const Cache = @import("util/cache.zig");
const XattrTable = @import("xattr.zig");
pub fn extract(
@@ -17,16 +21,204 @@ pub fn extract(
inode: Inode,
ext_loc: []const u8,
options: ExtractionOptions,
) !void {}
) !void {
const path = std.mem.trim(ext_loc, "/");
pub fn extractReal(
var id_table: Lookup.Table(u16) = .init(alloc, data, decomp, super.id_start, super.id_count);
defer id_table.deinit();
var xattr_table: XattrTable = .init(alloc, data, decomp, super.xattr_start);
defer xattr_table.deinit();
var buf: [150]ReturnUnion = undefined;
var sel: Io.Select(ReturnUnion) = .init(io, &buf);
defer while (sel.cancel()) |res|
switch (res) {
.path => |p| {
const path_return = p catch continue;
if (path_return.path.len != path.len)
alloc.free(path_return.path);
},
else => {},
};
var loop = io.async(io, finishLoop, .{ alloc, io, &sel, &id_table, &xattr_table, options });
var cache: Cache = .init(alloc, data, decomp);
defer cache.deinit();
var frag_table: Lookup.Table(Lookup.FragEntry) = .init(alloc, data, decomp, super.frag_start, super.frag_count);
defer frag_table.deinit();
switch (inode.hdr.type) {
.file, .ext_file => try sel.async(
.path,
extractFile,
.{ alloc, io, data, decomp, &cache, &frag_table, inode, path, true },
),
.dir, .ext_dir => try sel.async(
.path,
extractDir,
.{ alloc, io, super, data, decomp, &sel, &cache, &frag_table, inode, path, true },
),
.symlink, .ext_symlink => try sel.async(
.void,
extractSymlink,
.{ alloc, io, inode, path, true },
),
else => try sel.async(
.path,
extractNod,
.{ alloc, io, inode, path, true },
),
}
try loop.await(io);
}
fn finishLoop(alloc: std.mem.Allocator, io: Io, id_table: *Lookup.Table(u16), xattr_table: *XattrTable, options: ExtractionOptions) !void {}
fn extractFile(
alloc: std.mem.Allocator,
io: Io,
data: []u8,
decomp: Decomp.Fn,
cache: *Cache,
frag_table: *Lookup.Table(Lookup.FragEntry),
inode: Inode,
path: []const u8,
origin: bool,
) Error!PathReturn {
defer if (!origin) inode.deinit(alloc);
errdefer alloc.free(path);
try io.checkCancel();
var ret: PathReturn = .{
.hdr = inode.hdr,
.path = path,
};
var ext: DataExtractor = switch (inode.data) {
.file => |f| blk: {
var rdr: DataExtractor = .init(data, decomp, super.block_size, f.blocks, f.size, f.block_start);
rdr.addCache(cache);
if (f.frag_idx == 0xFFFFFFFF) break :blk rdr;
const entry: Lookup.FragEntry = try frag_table.get(io, f.frag_idx);
if (entry.size.uncompressed) {
rdr.addFrag(data[entry.block_start..][0..entry.size.size]);
} else {
rdr.addFrag(try cache.get(io, entry.block_start, entry.size));
}
},
.ext_file => |f| blk: {
if (f.xattr_idx != 0xFFFFFFFF) ret.xattr_idx = f.xattr_idx;
var rdr: DataExtractor = .init(data, decomp, super.block_size, f.blocks, f.size, f.block_start);
rdr.addCache(cache);
if (f.frag_idx == 0xFFFFFFFF) break :blk rdr;
const entry: Lookup.FragEntry = try frag_table.get(io, f.frag_idx);
if (entry.size.uncompressed) {
rdr.addFrag(data[entry.block_start..][0..entry.size.size]);
} else {
rdr.addFrag(try cache.get(io, entry.block_start, entry.size));
}
},
else => unreachable,
};
var atomic = try Io.Dir.cwd().createFileAtomic(io, path, .{});
defer atomic.deinit(io);
try ext.extractAsync(alloc, io, atomic.file);
try atomic.link(io);
return ret;
}
fn extractDir(
alloc: std.mem.Allocator,
io: Io,
super: Superblock,
data: []u8,
decomp: Decomp.Fn,
frag_table: Lookup.Table(u64),
sel: *Io.Select(ReturnUnion),
cache: *Cache,
frag_table: *Lookup.Table(Lookup.FragEntry),
inode: Inode,
ext_loc: []const u8,
options: ExtractionOptions,
) !void {}
path: []const u8,
origin: bool,
) Error!PathReturn {
defer if (!origin) inode.deinit(alloc);
errdefer alloc.free(path);
var ret: PathReturn = .{
.hdr = inode.hdr,
.path = path,
};
try Io.Dir.cwd().createDirPath(io, path);
var dir: Directory = switch (inode.data) {
.dir => |d| blk: {
var meta: MetadataReader = .init(alloc, data, decomp, d.block_start);
try meta.interface.discardAll(d.block_offset);
break :blk Directory.init(alloc, &meta.interface, d.size);
},
.ext_dir => |d| blk: {
if (d.xattr_idx != 0xFFFFFFFF) ret.xattr_idx = d.xattr_idx;
var meta: MetadataReader = .init(alloc, data, decomp, d.block_start);
try meta.interface.discardAll(d.block_offset);
break :blk Directory.init(alloc, &meta.interface, d.size);
},
else => unreachable,
};
defer dir.deinit(alloc);
for (dir.entries) |entry| {
var new_inode: Inode = try .initEntry(alloc, data, decomp, super.inode_start, super.block_size, entry);
const new_path = std.mem.concat(alloc, &.{ path, "/", entry.name }) catch |err| {
new_inode.deinit(alloc);
return err;
};
sel.async();
}
}
fn extractSymlink(alloc: std.mem.Allocator, io: Io, inode: Inode, path: []const u8, origin: bool) Error!void {
defer 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, .{});
}
// Types
const ReturnUnion = union(enum) {
path: Error!PathReturn,
dir: Error!PathReturn,
void: Error!void,
};
const Error = error{} || Decomp.Error || Directory.Error || DataExtractor.Error;
const PathReturn = struct {
hdr: Inode.Header,
path: []const u8,
xattr_idx: ?u32 = null,
};
+201 -6
View File
@@ -1,11 +1,15 @@
const std = @import("std");
const Io = std.Io;
const DataReader = @import("data/reader.zig");
const Decomp = @import("decomp.zig");
const Directory = @import("directory.zig");
const ExtractionOptions = @import("options.zig");
const Inode = @import("inode.zig");
const Lookup = @import("lookup.zig");
const MetadaReader = @import("meta_rdr.zig");
const Superblock = @import("archive.zig").Superblock;
const Cache = @import("util/cache.zig");
const XattrTable = @import("xattr.zig");
pub fn extract(
@@ -17,7 +21,37 @@ pub fn extract(
inode: Inode,
ext_loc: []const u8,
options: ExtractionOptions,
) !void {}
) !void {
const path = std.mem.trim(ext_loc, "/");
var cache: Cache = .init(alloc, data, decomp);
defer cache.deinit();
var frag_table: Lookup.Table(Lookup.FragEntry) = .init(alloc, data, decomp, super.frag_start, super.frag_count);
defer frag_table.deinit();
var id_table: Lookup.Table(u16) = .init(alloc, data, decomp, super.id_start, super.id_count);
defer id_table.deinit();
var xattr_table: XattrTable = .init(alloc, data, decomp, super.xattr_start);
defer xattr_table.deinit();
return extractReal(
alloc,
io,
super,
data,
decomp,
&cache,
&frag_table,
&id_table,
&xattr_table,
inode,
path,
options,
true,
);
}
pub fn extractReal(
alloc: std.mem.Allocator,
@@ -25,10 +59,171 @@ pub fn extractReal(
super: Superblock,
data: []u8,
decomp: Decomp.Fn,
frag_table: Lookup.Table(u64),
id_table: Lookup.Table(u16),
xattr_table: XattrTable,
cache: *Cache,
frag_table: *Lookup.Table(Lookup.FragEntry),
id_table: *Lookup.Table(u16),
xattr_table: *XattrTable,
inode: Inode,
ext_loc: []const u8,
path: []const u8,
options: ExtractionOptions,
) !void {}
origin: bool,
) !void {
defer if (!origin) {
alloc.free(path);
inode.deinit(alloc);
};
var xattr_idx: ?u32 = null;
switch (inode.data) {
.dir, .ext_dir => {
try Io.Dir.cwd().createDirPath(io, path);
var dir: Directory = switch (inode.data) {
.dir => |d| blk: {
var meta: MetadaReader = .init(alloc, data, decomp, d.block_start);
try meta.interface.discardAll(d.block_offset);
break :blk Directory.init(alloc, &meta.interface, d.size);
},
.ext_dir => |d| blk: {
if (d.xattr_idx != 0xFFFFFFFF) xattr_idx = d.xattr_idx;
var meta: MetadaReader = .init(alloc, data, decomp, d.block_start);
try meta.interface.discardAll(d.block_offset);
break :blk Directory.init(alloc, &meta.interface, d.size);
},
else => unreachable,
};
defer dir.deinit(alloc);
for (dir.entries) |entry| {
var new_inode: Inode = try .initEntry(alloc, data, decomp, super.inode_start, super.block_size, entry);
const new_path = std.mem.concat(alloc, &.{ path, "/", entry.name }) catch |err| {
new_inode.deinit(alloc);
return err;
};
try extractReal(alloc, io, super, data, decomp, cache, frag_table, id_table, xattr_table, new_inode, new_path, options, false);
}
},
.file, .ext_file => {
var rdr: DataReader = switch (inode.data) {
.file => |f| blk: {
var rdr: DataReader = .init(alloc, data, decomp, super.block_size, f.blocks, f.size, f.block_start);
rdr.addCache(cache);
if (f.frag_idx == 0xFFFFFFFF) break :blk rdr;
const entry: Lookup.FragEntry = try frag_table.get(io, f.frag_idx);
if (entry.size.uncompressed) {
rdr.addFrag(data[entry.block_start..][0..entry.size.size]);
} else {
rdr.addFrag(try cache.get(io, entry.block_start, entry.size));
}
},
.ext_file => |f| blk: {
if (f.xattr_idx != 0xFFFFFFFF) xattr_idx = f.xattr_idx;
var rdr: DataReader = .init(alloc, data, decomp, super.block_size, f.blocks, f.size, f.block_start);
rdr.addCache(cache);
if (f.frag_idx == 0xFFFFFFFF) break :blk rdr;
const entry: Lookup.FragEntry = try frag_table.get(io, f.frag_idx);
if (entry.size.uncompressed) {
rdr.addFrag(data[entry.block_start..][0..entry.size.size]);
} else {
rdr.addFrag(try cache.get(io, entry.block_start, entry.size));
}
},
else => unreachable,
};
var atomic = try Io.Dir.cwd().createFileAtomic(io, path, .{});
defer atomic.deinit(io);
var writer = atomic.file.writer(io, &[0]u8{});
_ = try rdr.interface.streamRemaining(&writer.interface);
try writer.flush();
try atomic.link(io);
},
.symlink => |s| return Io.Dir.cwd().symLink(io, s.target, path, .{}),
.ext_symlink => |s| return Io.Dir.cwd().symLink(io, s.target, path, .{}),
else => {
var dev: u32 = 0;
var mode: u32 = undefined;
const DT = std.posix.DT;
switch (inode.data) {
.char_dev => |d| {
dev = d.device;
mode = DT.CHR;
},
.block_dev => |d| {
dev = d.device;
mode = DT.BLK;
},
.ext_char_dev => |d| {
if (d.xattr_idx != 0xFFFFFFFF) xattr_idx = d.xattr_idx;
dev = d.device;
mode = DT.CHR;
},
.ext_block_dev => |d| {
if (d.xattr_idx != 0xFFFFFFFF) xattr_idx = d.xattr_idx;
dev = d.device;
mode = DT.BLK;
},
.fifo => mode = DT.FIFO,
.socket => mode = DT.SOCK,
.ext_fifo => |i| {
if (i.xattr_idx != 0xFFFFFFFF) xattr_idx = i.xattr_idx;
mode = DT.FIFO;
},
.ext_socket => |i| {
if (i.xattr_idx != 0xFFFFFFFF) xattr_idx = i.xattr_idx;
mode = DT.SOCK;
},
else => unreachable,
}
const sentinel_path = try alloc.dupeSentinel(u8, path, 0);
defer alloc.free(sentinel_path);
const res = std.os.linux.mknod(sentinel_path, mode, dev);
if (res != 0)
return error.MknodError;
},
}
if (options.ignore_permissions and (options.ignore_xattr or xattr_idx == null)) return;
var fil: Io.File = try Io.Dir.cwd().openFile(io, path, .{});
defer fil.close(io);
if (!options.ignore_permissions) {
try fil.setTimestamps(io, .{
.modify_timestamp = .init(Io.Timestamp.fromNanoseconds(@as(i96, @intCast(inode.hdr.mod_time)) * std.time.ns_per_s)),
});
try fil.setPermissions(io, @enumFromInt(inode.hdr.permissions));
try fil.setOwner(io, try id_table.get(io, inode.hdr.uid_idx), try id_table.get(io, inode.hdr.gid_idx));
}
if (!options.ignore_xattr and xattr_idx != null) {
const xattr = try xattr_table.get(alloc, io, xattr_idx.?);
defer xattr.deinit(alloc);
for (xattr.kvs) |kv| {
const res = std.os.linux.fsetxattr(fil.handle, kv.key, kv.value, kv.value.len, 0);
if (res != 0)
return error.SetXattrError;
}
}
}
+9
View File
@@ -1,6 +1,7 @@
const std = @import("std");
const Io = std.Io;
const DataBlock = @import("inode.zig").DataBlock;
const Decomp = @import("decomp.zig");
const MetadataReader = @import("meta_rdr.zig");
const ProtectedMap = @import("util/protected_map.zig");
@@ -57,3 +58,11 @@ pub fn Table(comptime T: anytype) type {
}
};
}
// Types
pub const FragEntry = extern struct {
block_start: u64,
size: DataBlock,
_: u32,
};
+52
View File
@@ -0,0 +1,52 @@
const std = @import("std");
const Io = std.Io;
const Decomp = @import("../decomp.zig");
const CacheMap = @import("protected_map.zig").ProtectedMap(u64, CachedBlock, decompressBlock);
const Cache = @This();
alloc: std.mem.Allocator,
data: []u8,
decomp: Decomp.Fn,
cache: CacheMap,
pub fn init(alloc: std.mem.Allocator, data: []u8, decomp: Decomp.Fn) Cache {
return .{
.alloc = alloc,
.data = data,
.decomp = decomp,
.cache = .init(alloc),
};
}
pub fn deinit(self: *Cache) void {
self.cache.deinit();
}
pub fn get(self: *Cache, io: Io, offset: u64, compressed_size: u32) Error![]u8 {
const res: CachedBlock = try self.cache.getOrPut(io, offset, .{ self.alloc, self.data, self.decomp, offset, compressed_size });
return res.block[0..res.size];
}
fn decompressBlock(alloc: std.mem.Allocator, data: []u8, decomp: Decomp.Fn, offset: u64, compressed_size: u32) !CachedBlock {
const block = data[offset..][0..compressed_size];
var new_cache: CachedBlock = undefined;
new_cache.size = @truncate(try decomp(alloc, block, &new_cache.block));
return new_cache;
}
// Types
pub const Error = CacheMap.Error;
const CachedBlock = struct {
block: [1024 * 1024]u8,
size: u32,
};
+4 -1
View File
@@ -28,7 +28,7 @@ pub fn ProtectedMap(comptime K: anytype, comptime T: anytype, comptime create_fn
self.map.deinit();
}
pub fn getOrPut(self: *Map, io: Io, key: K, create_fn_args: std.meta.ArgsTuple(create_fn)) !*T {
pub fn getOrPut(self: *Map, io: Io, key: K, create_fn_args: std.meta.ArgsTuple(create_fn)) Error!*T {
{
try self.mut.lockShared(io);
defer self.mut.unlockShared(io);
@@ -70,6 +70,9 @@ pub fn ProtectedMap(comptime K: anytype, comptime T: anytype, comptime create_fn
// Map Types
pub const Error = error{ Canceled, OutOfMemory } ||
if (@TypeOf(CreateError) == void) error{} else CreateError;
const CreateError: type = switch (@typeInfo(@typeInfo(create_fn).@"fn".return_type)) {
.error_union => |e| e.error_set,
else => void,
+129
View File
@@ -1,3 +1,132 @@
const std = @import("std");
const Io = std.Io;
const Decomp = @import("decomp.zig");
const Lookup = @import("lookup.zig");
const MetadataReader = @import("meta_rdr.zig");
const Ref = @import("inode.zig").Ref;
const XattrTable = @This();
table_start: u64,
table: Lookup.Table(Entry),
pub fn init(alloc: std.mem.Allocator, data: []u8, decomp: Decomp.Fn, table_start: u64) XattrTable {
const start = std.mem.readInt(u64, data[table_start..][0..8], .little);
const table_num = std.mem.readInt(u32, data[table_start + 8 ..][0..4], .little);
return .{
.table_start = start,
.table = .init(alloc, data, decomp, table_start + 16, table_num),
};
}
pub fn deinit(self: *XattrTable) void {
self.table.deinit();
}
pub fn get(self: *XattrTable, alloc: std.mem.Allocator, io: Io, idx: u32) !Xattr {
const entry: Entry = try self.table.get(io, idx);
const out: std.ArrayList(KeyValue) = try .initCapacity(alloc, entry.count);
errdefer {
for (out.items) |kv|
kv.deinit(alloc);
out.deinit(alloc);
}
var meta: MetadataReader = .init(alloc, self.table.data, self.table.decomp, self.table_start + entry.ref.block_start);
try meta.interface.discardAll(entry.ref.block_offset);
for (0..entry.count) |_| {
var key_entry: KeyEntry = undefined;
try meta.interface.readSliceEndian(KeyEntry, @ptrCast(&key_entry), .little);
const prefix = switch (key_entry.type.type) {
.user => "user.",
.trusted => "trusted.",
.security => "security.",
};
const key = try alloc.allocSentinel(u8, key_entry.name_size + prefix.len, 0);
errdefer alloc.free(key);
@memcpy(key[0..prefix.len], prefix);
try meta.interface.readSliceEndian(u8, key[prefix.len..][0..key_entry.name_size], .little);
const value = if (key_entry.type.out_of_line) blk: {
try meta.interface.discardAll(4);
var ref: Ref = undefined;
try meta.interface.readSliceEndian(Ref, @ptrCast(&ref), .little);
var value_meta: MetadataReader = .init(alloc, self.table.data, self.table.decomp, self.table_start + ref.block_start);
try value_meta.interface.discardAll(ref.block_offset);
break :blk try readValue(alloc, &value_meta.interface);
} else try readValue(alloc, &meta.interface);
const ptr = out.addOneAssumeCapacity();
ptr.* = .{
.key = key,
.value = value,
};
}
}
fn readValue(alloc: std.mem.Allocator, rdr: *Io.Reader) ![]u8 {
var size: u32 = undefined;
try rdr.readSliceEndian(u32, @ptrCast(&size), .little);
const value = try alloc.alloc(u8, size);
errdefer alloc.free(value);
try rdr.readSliceEndian(u8, value, .little);
return value;
}
// Types
const Xattr = struct {
kvs: []KeyValue,
pub fn deinit(self: Xattr, alloc: std.mem.Allocator) void {
for (self.kvs) |kv|
kv.deinit(alloc);
alloc.free(self.kvs);
}
};
const KeyValue = struct {
key: [:0]const u8,
value: []const u8,
pub fn deinit(self: KeyValue, alloc: std.mem.Allocator) void {
alloc.free(self.key);
alloc.free(self.value);
}
};
const KeyEntry = extern struct {
type: packed struct(u16) {
type: enum(u8) {
user,
trusted,
security,
},
out_of_line: bool,
_: u7,
},
name_size: u16,
};
const Entry = extern struct {
ref: Ref,
count: u32,
size: u32,
};