Finished work (theoretically) on single threaded extraction
Started work on Multi-threaded extraction Added decompression cache. Added XattrTable
This commit is contained in:
@@ -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
@@ -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
@@ -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
@@ -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 {
|
||||
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
@@ -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
@@ -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,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user