Remove DecompMgr in favor of a much simpler fn ptr.
Moved more functionality to Inode instead of File. Started doing some optimization around allocation. Slight rework of ExtractionOptions.
This commit is contained in:
@@ -4,4 +4,4 @@ This is my experiments to learn Zig. Might amount to something. Might not.
|
|||||||
|
|
||||||
## Current State
|
## Current State
|
||||||
|
|
||||||
Kinda works as a library, but currently has known memory leaks. `unsquashfs` is missing a lot of features (and will probably never match the official unsquashfs). Extraction is stupidly slow and uses too many resources.
|
Kinda works as a library, but currently has known memory leaks. `unsquashfs` is missing a lot of features (and will probably never match the official unsquashfs). Extraction is stupidly slow and uses too many resources. Only properly work on Linux, any other OSes probably won't work fully.
|
||||||
|
|||||||
+13
-8
@@ -4,7 +4,7 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const File = std.fs.File;
|
const File = std.fs.File;
|
||||||
|
|
||||||
const DecompMgr = @import("decomp.zig");
|
const Decomp = @import("decomp.zig");
|
||||||
const ExtractionOptions = @import("options.zig");
|
const ExtractionOptions = @import("options.zig");
|
||||||
const Inode = @import("inode.zig");
|
const Inode = @import("inode.zig");
|
||||||
const InodeRef = Inode.Ref;
|
const InodeRef = Inode.Ref;
|
||||||
@@ -40,7 +40,7 @@ super: Superblock,
|
|||||||
|
|
||||||
setup: bool = false,
|
setup: bool = false,
|
||||||
|
|
||||||
decomp: DecompMgr = undefined,
|
decomp: Decomp.DecompFn,
|
||||||
|
|
||||||
frag_table: Table(FragEntry) = undefined,
|
frag_table: Table(FragEntry) = undefined,
|
||||||
id_table: Table(u16) = undefined,
|
id_table: Table(u16) = undefined,
|
||||||
@@ -72,6 +72,13 @@ pub fn initAdvanced(alloc: std.mem.Allocator, fil: File, offset: u64, threads: u
|
|||||||
// .fixed_buf = fixed_buf,
|
// .fixed_buf = fixed_buf,
|
||||||
.thread_count = threads,
|
.thread_count = threads,
|
||||||
.fil = .init(fil, offset),
|
.fil = .init(fil, offset),
|
||||||
|
.decomp = switch (super.compression) {
|
||||||
|
.gzip => Decomp.gzipDecompress,
|
||||||
|
.lzma => Decomp.lzmaDecompress,
|
||||||
|
.xz => Decomp.xzDecompress,
|
||||||
|
.zstd => Decomp.zstdDecompress,
|
||||||
|
else => return error.UnsupportedCompressionType,
|
||||||
|
},
|
||||||
|
|
||||||
.super = super,
|
.super = super,
|
||||||
};
|
};
|
||||||
@@ -79,7 +86,6 @@ pub fn initAdvanced(alloc: std.mem.Allocator, fil: File, offset: u64, threads: u
|
|||||||
pub fn deinit(self: *Archive) void {
|
pub fn deinit(self: *Archive) void {
|
||||||
// self.parent_alloc.free(self.fixed_buf);
|
// self.parent_alloc.free(self.fixed_buf);
|
||||||
if (self.setup) {
|
if (self.setup) {
|
||||||
self.decomp.deinit();
|
|
||||||
self.frag_table.deinit();
|
self.frag_table.deinit();
|
||||||
self.export_table.deinit();
|
self.export_table.deinit();
|
||||||
self.id_table.deinit();
|
self.id_table.deinit();
|
||||||
@@ -92,10 +98,9 @@ pub fn allocator(self: *Archive) std.mem.Allocator {
|
|||||||
|
|
||||||
fn setupValues(self: *Archive) !void {
|
fn setupValues(self: *Archive) !void {
|
||||||
const alloc = self.allocator();
|
const alloc = self.allocator();
|
||||||
self.decomp = try .init(alloc, self.super.compression, self.super.block_size, self.thread_count);
|
self.frag_table = try .init(alloc, self.fil, self.decomp, self.super.frag_start, self.super.frag_count);
|
||||||
self.frag_table = try .init(alloc, self.fil, &self.decomp, self.super.frag_start, self.super.frag_count);
|
self.id_table = try .init(alloc, self.fil, self.decomp, self.super.id_start, self.super.id_count);
|
||||||
self.id_table = try .init(alloc, self.fil, &self.decomp, self.super.id_start, self.super.id_count);
|
self.export_table = try .init(alloc, self.fil, self.decomp, self.super.export_start, self.super.inode_count);
|
||||||
self.export_table = try .init(alloc, self.fil, &self.decomp, self.super.export_start, self.super.inode_count);
|
|
||||||
self.setup = true;
|
self.setup = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,7 +126,7 @@ pub fn inode(self: *Archive, num: u32) !Inode {
|
|||||||
pub fn root(self: *Archive) !SfsFile {
|
pub fn root(self: *Archive) !SfsFile {
|
||||||
if (!self.setup) try self.setupValues();
|
if (!self.setup) try self.setupValues();
|
||||||
var rdr = try self.fil.readerAt(self.super.root_ref.block_start + self.super.inode_start, &[0]u8{});
|
var rdr = try self.fil.readerAt(self.super.root_ref.block_start + self.super.inode_start, &[0]u8{});
|
||||||
var meta: MetadataReader = .init(self.allocator(), &rdr.interface, &self.decomp);
|
var meta: MetadataReader = .init(self.allocator(), &rdr.interface, self.decomp);
|
||||||
try meta.interface.discardAll(self.super.root_ref.block_offset);
|
try meta.interface.discardAll(self.super.root_ref.block_offset);
|
||||||
const in: Inode = try .read(self.allocator(), &meta.interface, self.super.block_size);
|
const in: Inode = try .read(self.allocator(), &meta.interface, self.super.block_size);
|
||||||
return .init(self, in, "");
|
return .init(self, in, "");
|
||||||
|
|||||||
+24
-198
@@ -1,21 +1,5 @@
|
|||||||
//! Decompression manager. Can decompress either from an Io.Reader or from a byte slice.
|
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const compress = std.compress;
|
|
||||||
const Reader = std.Io.Reader;
|
const Reader = std.Io.Reader;
|
||||||
const Thread = std.Thread;
|
|
||||||
const Futex = Thread.Futex;
|
|
||||||
const Mutex = Thread.Mutex;
|
|
||||||
const Condition = Thread.Condition;
|
|
||||||
const Node = std.DoublyLinkedList.Node;
|
|
||||||
|
|
||||||
const Atomic = std.atomic.Value(u32);
|
|
||||||
|
|
||||||
const DecompError = error{
|
|
||||||
ThreadClosed,
|
|
||||||
LzoUnsupported,
|
|
||||||
Lz4Unsupported,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const CompressionType = enum(u16) {
|
pub const CompressionType = enum(u16) {
|
||||||
gzip = 1,
|
gzip = 1,
|
||||||
@@ -26,193 +10,35 @@ pub const CompressionType = enum(u16) {
|
|||||||
zstd,
|
zstd,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const DecompThread = struct {
|
pub const DecompFn = *const fn (alloc: std.mem.Allocator, in: []u8, out: []u8) anyerror!usize; // TODO: replace anyerror to definitive error types.
|
||||||
mgr: *DecompMgr,
|
|
||||||
|
|
||||||
/// Current thread status & signal value via Futex.
|
// pub const DecompressError = error{
|
||||||
/// 0 - Unstarted, 1 - Waiting, 2 - Working, 3 - Closed,
|
// ReadFailed,
|
||||||
status: Atomic = .{ .raw = 0 },
|
// anyerror,
|
||||||
thr: Thread = undefined,
|
// };
|
||||||
node: Node = .{},
|
|
||||||
buf: []u8,
|
|
||||||
|
|
||||||
dat: []u8 = &[0]u8{},
|
pub fn gzipDecompress(alloc: std.mem.Allocator, in: []u8, out: []u8) anyerror!usize {
|
||||||
rdr: ?*Reader = null,
|
_ = alloc;
|
||||||
res: []u8 = &[0]u8{},
|
var rdr: Reader = .fixed(in);
|
||||||
res_size: anyerror!usize = 0,
|
var decomp = std.compress.flate.Decompress.init(&rdr, .zlib, &[0]u8{});
|
||||||
|
return decomp.reader.readSliceShort(out);
|
||||||
pub fn init(mgr: *DecompMgr) !DecompThread {
|
|
||||||
return .{
|
|
||||||
.mgr = mgr,
|
|
||||||
.buf = switch (mgr.comp_type) {
|
|
||||||
.gzip => try mgr.alloc.alloc(u8, compress.flate.max_window_len),
|
|
||||||
.zstd => try mgr.alloc.alloc(u8, compress.zstd.default_window_len + compress.zstd.block_size_max),
|
|
||||||
.lzma, .xz => &[0]u8{},
|
|
||||||
else => unreachable,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn close(self: *DecompThread) void {
|
|
||||||
if (self.status.raw == 0) return;
|
|
||||||
while (self.status.raw == 2) Futex.wait(&self.status, 2);
|
|
||||||
self.status.store(3, .release);
|
|
||||||
Futex.wake(&self.status, 1);
|
|
||||||
self.thr.join();
|
|
||||||
self.mgr.alloc.free(self.buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn submitData(self: *DecompThread, dat: []u8, res: []u8) anyerror!usize {
|
|
||||||
if (self.status.raw == 3) return DecompError.ThreadClosed;
|
|
||||||
if (self.status.raw == 0) {
|
|
||||||
self.thr = try .spawn(.{}, thread, .{self});
|
|
||||||
}
|
|
||||||
self.dat = dat;
|
|
||||||
defer self.dat = &[0]u8{};
|
|
||||||
self.res = res;
|
|
||||||
self.status.raw = 2;
|
|
||||||
while (self.status.raw == 2) Futex.wait(&self.status, 2);
|
|
||||||
return self.res_size;
|
|
||||||
}
|
|
||||||
pub fn submitReader(self: *DecompThread, rdr: *Reader, res: []u8) anyerror!usize {
|
|
||||||
if (self.status.raw == 3) return DecompError.ThreadClosed;
|
|
||||||
if (self.status.raw == 0) {
|
|
||||||
self.thr = try .spawn(.{}, thread, .{self});
|
|
||||||
}
|
|
||||||
self.rdr = rdr;
|
|
||||||
defer self.rdr = null;
|
|
||||||
self.res = res;
|
|
||||||
self.status.store(2, .release);
|
|
||||||
Futex.wake(&self.status, 1);
|
|
||||||
while (self.status.raw == 2) Futex.wait(&self.status, 2);
|
|
||||||
return self.res_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn thread(self: *DecompThread) void {
|
|
||||||
const comp_type = self.mgr.comp_type;
|
|
||||||
while (self.status.raw != 3) {
|
|
||||||
while (self.status.raw == 1) Futex.wait(&self.status, 1);
|
|
||||||
if (self.status.raw == 3) return;
|
|
||||||
var dat_rdr: Reader = .fixed(self.dat);
|
|
||||||
var rdr: *Reader = if (self.rdr != null) self.rdr.? else &dat_rdr;
|
|
||||||
self.res_size = blk: switch (comp_type) {
|
|
||||||
.gzip => {
|
|
||||||
var decomp_rdr = compress.flate.Decompress.init(rdr, .zlib, self.buf);
|
|
||||||
break :blk decomp_rdr.reader.readSliceShort(self.res) catch |err| {
|
|
||||||
break :blk decomp_rdr.err orelse err;
|
|
||||||
};
|
|
||||||
},
|
|
||||||
.lzma => {
|
|
||||||
var decomp_rdr = compress.lzma.decompress(self.mgr.alloc, rdr.adaptToOldInterface()) catch |err| {
|
|
||||||
break :blk err;
|
|
||||||
};
|
|
||||||
break :blk decomp_rdr.read(self.res);
|
|
||||||
},
|
|
||||||
.xz => {
|
|
||||||
var decomp_rdr = compress.xz.decompress(self.mgr.alloc, rdr.adaptToOldInterface()) catch |err| {
|
|
||||||
break :blk err;
|
|
||||||
};
|
|
||||||
break :blk decomp_rdr.read(self.res);
|
|
||||||
},
|
|
||||||
.zstd => {
|
|
||||||
var decomp_rdr = compress.zstd.Decompress.init(rdr, self.buf, .{});
|
|
||||||
break :blk decomp_rdr.reader.readSliceShort(self.res) catch |err| {
|
|
||||||
break :blk decomp_rdr.err orelse err;
|
|
||||||
};
|
|
||||||
},
|
|
||||||
else => unreachable,
|
|
||||||
};
|
|
||||||
const orig = self.status.swap(1, .release);
|
|
||||||
Futex.wake(&self.status, 1);
|
|
||||||
if (orig == 3) return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const DecompMgr = @This();
|
|
||||||
|
|
||||||
alloc: std.mem.Allocator,
|
|
||||||
comp_type: CompressionType,
|
|
||||||
block_size: u32,
|
|
||||||
|
|
||||||
threads: []DecompThread,
|
|
||||||
queue: std.DoublyLinkedList = .{},
|
|
||||||
mut: Mutex = .{},
|
|
||||||
cond: Condition = .{},
|
|
||||||
to_start: usize,
|
|
||||||
|
|
||||||
pub fn init(alloc: std.mem.Allocator, comp_type: CompressionType, block_size: u32, threads: usize) !DecompMgr {
|
|
||||||
return switch (comp_type) {
|
|
||||||
.lzo => DecompError.LzoUnsupported,
|
|
||||||
.lz4 => DecompError.Lz4Unsupported,
|
|
||||||
else => .{
|
|
||||||
.alloc = alloc,
|
|
||||||
.comp_type = comp_type,
|
|
||||||
.block_size = block_size,
|
|
||||||
.threads = try alloc.alloc(DecompThread, threads),
|
|
||||||
.to_start = threads,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(self: DecompMgr) void {
|
pub fn lzmaDecompress(alloc: std.mem.Allocator, in: []u8, out: []u8) anyerror!usize {
|
||||||
for (self.threads[self.to_start..]) |*t| {
|
var rdr: Reader = .fixed(in);
|
||||||
t.close();
|
var decomp = try std.compress.lzma.decompress(alloc, rdr.adaptToOldInterface());
|
||||||
}
|
return decomp.read(out);
|
||||||
self.alloc.free(self.threads);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn decompSlice(self: *DecompMgr, dat: []u8, res: []u8) !usize {
|
pub fn xzDecompress(alloc: std.mem.Allocator, in: []u8, out: []u8) anyerror!usize {
|
||||||
self.mut.lock();
|
var rdr: Reader = .fixed(in);
|
||||||
var thr: *DecompThread = undefined;
|
var decomp = try std.compress.xz.decompress(alloc, rdr.adaptToOldInterface());
|
||||||
var node = self.queue.popFirst();
|
return decomp.read(out);
|
||||||
if (self.node != null) {
|
|
||||||
self.mut.unlock();
|
|
||||||
thr = @fieldParentPtr("node", node.?);
|
|
||||||
} else blk: {
|
|
||||||
defer self.mut.unlock();
|
|
||||||
if (self.to_start > 0) {
|
|
||||||
self.threads[self.to_start - 1] = .init(self);
|
|
||||||
thr = &self.threads[self.to_start - 1];
|
|
||||||
self.to_start -= 1;
|
|
||||||
break :blk;
|
|
||||||
}
|
|
||||||
while (node == null) {
|
|
||||||
self.cond.wait(&self.mut);
|
|
||||||
node = self.queue.popFirst();
|
|
||||||
}
|
|
||||||
thr = @fieldParentPtr("node", node.?);
|
|
||||||
}
|
|
||||||
defer {
|
|
||||||
self.queue.append(&thr.node);
|
|
||||||
self.cond.signal();
|
|
||||||
}
|
|
||||||
return thr.submitData(dat, res);
|
|
||||||
}
|
}
|
||||||
pub fn decompReader(self: *DecompMgr, rdr: *Reader, res: []u8) !usize {
|
|
||||||
self.mut.lock();
|
pub fn zstdDecompress(alloc: std.mem.Allocator, in: []u8, out: []u8) anyerror!usize {
|
||||||
var thr: *DecompThread = undefined;
|
_ = alloc;
|
||||||
var node = self.queue.popFirst();
|
var rdr: Reader = .fixed(in);
|
||||||
if (node != null) {
|
var decomp = std.compress.zstd.Decompress.init(&rdr, &[0]u8{}, .{});
|
||||||
self.mut.unlock();
|
return decomp.reader.readSliceShort(out);
|
||||||
thr = @fieldParentPtr("node", node.?);
|
|
||||||
} else blk: {
|
|
||||||
defer self.mut.unlock();
|
|
||||||
if (self.to_start > 0) {
|
|
||||||
self.threads[self.to_start - 1] = try .init(self);
|
|
||||||
thr = &self.threads[self.to_start - 1];
|
|
||||||
self.to_start -= 1;
|
|
||||||
break :blk;
|
|
||||||
}
|
|
||||||
while (node == null) {
|
|
||||||
self.cond.wait(&self.mut);
|
|
||||||
node = self.queue.popFirst();
|
|
||||||
}
|
|
||||||
thr = @fieldParentPtr("node", node.?);
|
|
||||||
}
|
|
||||||
defer {
|
|
||||||
self.queue.append(&thr.node);
|
|
||||||
self.cond.signal();
|
|
||||||
}
|
|
||||||
return thr.submitReader(rdr, res);
|
|
||||||
}
|
}
|
||||||
|
|||||||
+9
-8
@@ -30,19 +30,20 @@ pub fn readDir(alloc: std.mem.Allocator, rdr: *Reader, size: u32) ![]Entry {
|
|||||||
var cur_red: u32 = 3; // start at 3 due to "." & ".." being counted in the dir size.
|
var cur_red: u32 = 3; // start at 3 due to "." & ".." being counted in the dir size.
|
||||||
var hdr: Header = undefined;
|
var hdr: Header = undefined;
|
||||||
var raw: RawEntry = undefined;
|
var raw: RawEntry = undefined;
|
||||||
var out: std.ArrayList(Entry) = .empty;
|
var out: std.ArrayList(Entry) = try .initCapacity(alloc, 25); // Start out with capacity instead of needing to allocate per header.
|
||||||
errdefer {
|
errdefer out.deinit(alloc);
|
||||||
for (out.items) |i|
|
|
||||||
i.deinit(alloc);
|
|
||||||
out.deinit(alloc);
|
|
||||||
}
|
|
||||||
while (cur_red < size) {
|
while (cur_red < size) {
|
||||||
try rdr.readSliceEndian(Header, @ptrCast(&hdr), .little);
|
try rdr.readSliceEndian(Header, @ptrCast(&hdr), .little);
|
||||||
cur_red += @sizeOf(Header);
|
cur_red += @sizeOf(Header);
|
||||||
try out.ensureUnusedCapacity(alloc, hdr.num + 1);
|
const count = hdr.count + 1;
|
||||||
for (0..hdr.count + 1) |_| {
|
if (out.capacity < count) {
|
||||||
|
// Make sure we have at least 25 capacity past current count.
|
||||||
|
try out.ensureUnusedCapacity(alloc, ((count % 25) + 2) * 25);
|
||||||
|
}
|
||||||
|
for (0..count) |_| {
|
||||||
try rdr.readSliceEndian(RawEntry, @ptrCast(&raw), .little);
|
try rdr.readSliceEndian(RawEntry, @ptrCast(&raw), .little);
|
||||||
const name = try alloc.alloc(u8, raw.name_size + 1);
|
const name = try alloc.alloc(u8, raw.name_size + 1);
|
||||||
|
errdefer alloc.free(name);
|
||||||
try rdr.readSliceEndian(u8, name, .little);
|
try rdr.readSliceEndian(u8, name, .little);
|
||||||
const val = out.addOneAssumeCapacity();
|
const val = out.addOneAssumeCapacity();
|
||||||
val.* = .{
|
val.* = .{
|
||||||
|
|||||||
+4
-298
@@ -56,28 +56,7 @@ pub fn deinit(self: SfsFile) void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn getEntries(self: SfsFile) ![]DirEntry {
|
fn getEntries(self: SfsFile) ![]DirEntry {
|
||||||
if (!self.isDir()) return FileError.NotDirectory;
|
return self.inode.dirEntries(self.archive);
|
||||||
var block_start: u32 = undefined;
|
|
||||||
var block_offset: u16 = undefined;
|
|
||||||
var size: u32 = undefined;
|
|
||||||
switch (self.inode.data) {
|
|
||||||
.dir => |d| {
|
|
||||||
block_start = d.block_start;
|
|
||||||
block_offset = d.block_offset;
|
|
||||||
size = d.size;
|
|
||||||
},
|
|
||||||
.ext_dir => |d| {
|
|
||||||
block_start = d.block_start;
|
|
||||||
block_offset = d.block_offset;
|
|
||||||
size = d.size;
|
|
||||||
},
|
|
||||||
else => unreachable,
|
|
||||||
}
|
|
||||||
var rdr = try self.archive.fil.readerAt(self.archive.super.dir_start + block_start, &[0]u8{});
|
|
||||||
const alloc = self.archive.allocator();
|
|
||||||
var meta: MetadataReader = .init(alloc, &rdr.interface, &self.archive.decomp);
|
|
||||||
try meta.interface.discardAll(block_offset);
|
|
||||||
return DirEntry.readDir(alloc, &meta.interface, size);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ownerUid(self: SfsFile) !u16 {
|
pub fn ownerUid(self: SfsFile) !u16 {
|
||||||
@@ -99,33 +78,7 @@ pub fn isRegular(self: SfsFile) bool {
|
|||||||
/// The returned DataReader will no longer work if the File's deinit function is called
|
/// The returned DataReader will no longer work if the File's deinit function is called
|
||||||
/// or, more specifically, it's inode's deinit function is called.
|
/// or, more specifically, it's inode's deinit function is called.
|
||||||
pub fn dataReader(self: SfsFile) !DataReader {
|
pub fn dataReader(self: SfsFile) !DataReader {
|
||||||
if (!self.isRegular()) return FileError.NotRegularFile;
|
return self.inode.dataReader(self.archive);
|
||||||
var frag_idx: u32 = undefined;
|
|
||||||
var frag_offset: u32 = undefined;
|
|
||||||
var size: u64 = undefined;
|
|
||||||
var blocks: []BlockSize = undefined;
|
|
||||||
var start: u64 = undefined;
|
|
||||||
switch (self.inode.data) {
|
|
||||||
.file => |f| {
|
|
||||||
frag_idx = f.frag_idx;
|
|
||||||
frag_offset = f.frag_block_offset;
|
|
||||||
size = f.size;
|
|
||||||
blocks = f.block_sizes;
|
|
||||||
start = f.block_start;
|
|
||||||
},
|
|
||||||
.ext_file => |f| {
|
|
||||||
frag_idx = f.frag_idx;
|
|
||||||
frag_offset = f.frag_block_offset;
|
|
||||||
size = f.size;
|
|
||||||
blocks = f.block_sizes;
|
|
||||||
start = f.block_start;
|
|
||||||
},
|
|
||||||
else => unreachable,
|
|
||||||
}
|
|
||||||
var out: DataReader = .init(self.archive, blocks, start, size);
|
|
||||||
if (frag_idx != 0xFFFFFFFF)
|
|
||||||
out.addFragment(try self.archive.frag(frag_idx), frag_offset);
|
|
||||||
return out;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn isDir(self: SfsFile) bool {
|
pub fn isDir(self: SfsFile) bool {
|
||||||
@@ -245,255 +198,8 @@ pub fn extract(self: *SfsFile, path: []const u8, options: ExtractionOptions) !vo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
defer if (ext_path.len > path.len) alloc.free(ext_path);
|
defer if (ext_path.len > path.len) alloc.free(ext_path);
|
||||||
var pool: std.Thread.Pool = undefined;
|
//TODO: switch to threaded version.
|
||||||
try pool.init(.{ .allocator = alloc, .n_jobs = 16 });
|
return self.inode.extractTo(self.archive, path, options);
|
||||||
var wg: WaitGroup = .{};
|
|
||||||
defer pool.deinit();
|
|
||||||
var err: ?anyerror = null;
|
|
||||||
wg.start();
|
|
||||||
self.extractReal(ext_path, options, &pool, &wg, &err, null);
|
|
||||||
wg.wait();
|
|
||||||
if (err != null) return err.?;
|
|
||||||
}
|
|
||||||
|
|
||||||
const ParentInfo = struct {
|
|
||||||
sfs_fil: SfsFile,
|
|
||||||
path: []const u8,
|
|
||||||
mut: *Mutex,
|
|
||||||
dir_wg: *WaitGroup,
|
|
||||||
parent_wg: *WaitGroup,
|
|
||||||
options: ExtractionOptions,
|
|
||||||
err: *?anyerror,
|
|
||||||
|
|
||||||
fn finish(self: *const ParentInfo) void {
|
|
||||||
self.mut.lock();
|
|
||||||
if (!self.dir_wg.isDone()) {
|
|
||||||
self.mut.unlock();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
self.mut.unlock();
|
|
||||||
std.debug.print("finishing dir {}: {s}\n", .{ self.sfs_fil.inode.hdr.num, self.sfs_fil.name });
|
|
||||||
self.sfs_fil.archive.allocator().destroy(self.mut);
|
|
||||||
self.sfs_fil.archive.allocator().destroy(self.dir_wg);
|
|
||||||
defer self.parent_wg.finish();
|
|
||||||
var fil = std.fs.cwd().openFile(self.path, .{}) catch |err| {
|
|
||||||
std.log.err("Error opening folder {s} to set permissions: {}\n", .{ self.path, err });
|
|
||||||
self.err.* = err;
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
defer fil.close();
|
|
||||||
self.sfs_fil.setPerm(fil, self.options) catch |err| {
|
|
||||||
std.log.err("Error setting permissions to {s}: {}\n", .{ self.path, err });
|
|
||||||
self.err.* = err;
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
fn extractReal(self: SfsFile, path: []const u8, options: ExtractionOptions, pol: *std.Thread.Pool, wg: *WaitGroup, out_err: *?anyerror, parent: ?ParentInfo) void {
|
|
||||||
std.log.info("Extracting {s} (inode {}) to {s}\n", .{ self.name, self.inode.hdr.num, path });
|
|
||||||
defer {
|
|
||||||
if (parent != null) {
|
|
||||||
parent.?.finish();
|
|
||||||
self.archive.allocator().free(path);
|
|
||||||
self.deinit();
|
|
||||||
} else {
|
|
||||||
wg.finish();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (out_err.* != null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
switch (self.inode.hdr.inode_type) {
|
|
||||||
.file, .ext_file => {
|
|
||||||
var fil = std.fs.cwd().createFile(path, .{}) catch |err| {
|
|
||||||
std.log.err("Error creating {s}: {}\n", .{ path, err });
|
|
||||||
out_err.* = err;
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
defer fil.close();
|
|
||||||
var dat_rdr = self.dataReader() catch |err| {
|
|
||||||
std.log.err("Error getting data reader for {s} (inode {}): {}\n", .{ self.name, self.inode.hdr.num, err });
|
|
||||||
out_err.* = err;
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
defer dat_rdr.deinit();
|
|
||||||
var wrt = fil.writer(&[0]u8{});
|
|
||||||
_ = dat_rdr.interface.streamRemaining(&wrt.interface) catch |err| {
|
|
||||||
std.log.err("Error writing data for {s} (inode {}) to {s}: {}\n", .{ self.name, self.inode.hdr.num, path, err });
|
|
||||||
out_err.* = wrt.err orelse err;
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
wrt.interface.flush() catch |err| {
|
|
||||||
std.log.err("Error flushing data for {s} (inode {}) to {s}: {}\n", .{ self.name, self.inode.hdr.num, path, err });
|
|
||||||
out_err.* = wrt.err orelse err;
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
self.setPerm(fil, options) catch |err| {
|
|
||||||
std.log.err("Error setting permissions/owner for {s}: {}\n", .{ path, err });
|
|
||||||
out_err.* = err;
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
},
|
|
||||||
.symlink, .ext_symlink => {
|
|
||||||
//TODO: deal with dereference symlink options
|
|
||||||
const target_path = self.symlinkPath() catch |err| {
|
|
||||||
std.log.err("Error getting symlink target path for {s} (inode {}): {}\n", .{ self.name, self.inode.hdr.num, err });
|
|
||||||
out_err.* = err;
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
std.fs.cwd().symLink(target_path, path, .{}) catch |err| {
|
|
||||||
std.log.err("Error creating {s}: {}\n", .{ path, err });
|
|
||||||
out_err.* = err;
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
// self.setPerm(fil, options) catch |err| {
|
|
||||||
// std.log.err("Error setting permissions/owner for {s}: {}\n", .{ path, err });
|
|
||||||
// out_err.* = err;
|
|
||||||
// return;
|
|
||||||
// };
|
|
||||||
},
|
|
||||||
.block_dev,
|
|
||||||
.char_dev,
|
|
||||||
.fifo,
|
|
||||||
.ext_block_dev,
|
|
||||||
.ext_char_dev,
|
|
||||||
.ext_fifo,
|
|
||||||
=> {
|
|
||||||
var mode: u32 = undefined;
|
|
||||||
var fil_dev: u32 = 0;
|
|
||||||
switch (self.inode.hdr.inode_type) {
|
|
||||||
.block_dev, .ext_block_dev => {
|
|
||||||
mode = std.posix.DT.BLK;
|
|
||||||
fil_dev = self.devNum() catch |err| {
|
|
||||||
std.log.err("Error getting device number for {s} (inode {}): {}\n", .{ self.name, self.inode.hdr.num, err });
|
|
||||||
out_err.* = err;
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
},
|
|
||||||
.char_dev, .ext_char_dev => {
|
|
||||||
mode = std.posix.DT.CHR;
|
|
||||||
fil_dev = self.devNum() catch |err| {
|
|
||||||
std.log.err("Error getting device number for {s} (inode {}): {}\n", .{ self.name, self.inode.hdr.num, err });
|
|
||||||
out_err.* = err;
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
},
|
|
||||||
else => mode = std.posix.DT.FIFO,
|
|
||||||
}
|
|
||||||
const res = std.os.linux.mknod(@ptrCast(path), mode, fil_dev);
|
|
||||||
if (res != 0) {
|
|
||||||
std.log.err("Error creating device file at {s} with code {}\n", .{ path, res });
|
|
||||||
out_err.* = error.MknodError;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const fil = std.fs.cwd().openFile(path, .{}) catch |err| {
|
|
||||||
std.log.err("Error openning {s} to set permissions: {}\n", .{ path, err });
|
|
||||||
out_err.* = err;
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
defer fil.close();
|
|
||||||
self.setPerm(fil, options) catch |err| {
|
|
||||||
std.log.err("Error setting permissions/owner for {s}: {}\n", .{ path, err });
|
|
||||||
out_err.* = err;
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
},
|
|
||||||
.dir, .ext_dir => {
|
|
||||||
std.debug.print("starting dir {}: {s}\n", .{ self.inode.hdr.num, self.name });
|
|
||||||
if (std.fs.cwd().statFile(path)) |stat| {
|
|
||||||
if (stat.kind != .directory) {
|
|
||||||
std.log.err("{s} exists and is not a folder\n", .{path});
|
|
||||||
out_err.* = FileError.ExtractionPathExists;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else |err| {
|
|
||||||
if (err == error.FileNotFound) {
|
|
||||||
std.fs.cwd().makeDir(path) catch |err_2| {
|
|
||||||
std.log.err("Error creating {s}: {}\n", .{ path, err_2 });
|
|
||||||
out_err.* = err;
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
std.log.err("Error checking if {s} exists: {}\n", .{ path, err });
|
|
||||||
out_err.* = err;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var dir_wg: *WaitGroup = self.archive.allocator().create(WaitGroup) catch |err| {
|
|
||||||
std.log.err("Error allocating waitgroup for {s} (inode {}): {}\n", .{ path, self.inode.hdr.num, err });
|
|
||||||
out_err.* = err;
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
const parent_info: ParentInfo = .{
|
|
||||||
.sfs_fil = self,
|
|
||||||
.path = path,
|
|
||||||
.mut = self.archive.allocator().create(Mutex) catch |err| {
|
|
||||||
std.log.err("Error allocating mutex for {s} (inode {}): {}\n", .{ path, self.inode.hdr.num, err });
|
|
||||||
out_err.* = err;
|
|
||||||
return;
|
|
||||||
},
|
|
||||||
.dir_wg = dir_wg,
|
|
||||||
.parent_wg = wg,
|
|
||||||
.options = options,
|
|
||||||
.err = out_err,
|
|
||||||
};
|
|
||||||
var iter: Iterator = self.iterate() catch |err| {
|
|
||||||
std.log.err("Error getting iterator for {s} (inode {}): {}\n", .{ path, self.inode.hdr.num, err });
|
|
||||||
out_err.* = err;
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
defer iter.deinit();
|
|
||||||
const path_has_end_sep = path[path.len - 1] == '/';
|
|
||||||
while (true) {
|
|
||||||
const iter_fil = iter.next() catch |err| {
|
|
||||||
std.log.err("Error getting next iterator value {s} (inode {}): {}\n", .{ path, self.inode.hdr.num, err });
|
|
||||||
out_err.* = err;
|
|
||||||
break;
|
|
||||||
};
|
|
||||||
if (iter_fil == null) break;
|
|
||||||
const fil = iter_fil.?;
|
|
||||||
dir_wg.start();
|
|
||||||
var path_len = path.len + fil.name.len;
|
|
||||||
if (!path_has_end_sep) path_len += 1;
|
|
||||||
var new_path = self.archive.allocator().alloc(u8, path_len) catch |err| {
|
|
||||||
std.log.err("Error allocating subpath for {s} (inode {}): {}\n", .{ path, self.inode.hdr.num, err });
|
|
||||||
out_err.* = err;
|
|
||||||
dir_wg.finish();
|
|
||||||
break;
|
|
||||||
};
|
|
||||||
@memcpy(new_path[0..path.len], path);
|
|
||||||
@memcpy(new_path[new_path.len - fil.name.len ..], fil.name);
|
|
||||||
if (!path_has_end_sep) new_path[path.len] = '/';
|
|
||||||
if (fil.isDir()) {
|
|
||||||
fil.extractReal(new_path, options, pol, wg, out_err, parent_info);
|
|
||||||
} else {
|
|
||||||
pol.spawn(extractReal, .{
|
|
||||||
fil,
|
|
||||||
new_path,
|
|
||||||
options,
|
|
||||||
pol,
|
|
||||||
wg,
|
|
||||||
out_err,
|
|
||||||
parent_info,
|
|
||||||
}) catch |err| {
|
|
||||||
std.log.err("Error starting sub-file extraction thread: {}\n", .{err});
|
|
||||||
out_err.* = err;
|
|
||||||
dir_wg.finish();
|
|
||||||
break;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
.socket, .ext_socket => {
|
|
||||||
std.log.info("Ignoring socket file {s} (inode {})\n", .{ self.name, self.inode.hdr.num });
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn setPerm(self: SfsFile, fil: File, options: ExtractionOptions) !void {
|
|
||||||
if (!options.ignoreOwner) try fil.chmod(self.inode.hdr.permissions);
|
|
||||||
if (!options.ignorePermissions) try fil.chown(try self.ownerUid(), try self.ownerGid());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Utility function.
|
/// Utility function.
|
||||||
|
|||||||
+217
@@ -2,10 +2,17 @@
|
|||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const Reader = std.Io.Reader;
|
const Reader = std.Io.Reader;
|
||||||
|
const WaitGroup = std.Thread.WaitGroup;
|
||||||
|
const Pool = std.Thread.Pool;
|
||||||
|
|
||||||
|
const Archive = @import("archive.zig");
|
||||||
|
const DirEntry = @import("dir_entry.zig");
|
||||||
|
const ExtractionOptions = @import("options.zig");
|
||||||
const dir = @import("inode_data/dir.zig");
|
const dir = @import("inode_data/dir.zig");
|
||||||
const file = @import("inode_data/file.zig");
|
const file = @import("inode_data/file.zig");
|
||||||
const misc = @import("inode_data/misc.zig");
|
const misc = @import("inode_data/misc.zig");
|
||||||
|
const DataReader = @import("util/data.zig");
|
||||||
|
const MetadataReader = @import("util/metadata.zig");
|
||||||
|
|
||||||
pub const Ref = packed struct {
|
pub const Ref = packed struct {
|
||||||
block_offset: u16,
|
block_offset: u16,
|
||||||
@@ -94,3 +101,213 @@ pub fn deinit(self: Inode, alloc: std.mem.Allocator) void {
|
|||||||
else => {},
|
else => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the data reader for a file inode.
|
||||||
|
pub fn dataReader(self: Inode, archive: *Archive) !DataReader {
|
||||||
|
return switch (self.hdr.inode_type) {
|
||||||
|
.file => readerFromData(archive, self.data.file),
|
||||||
|
.ext_file => readerFromData(archive, self.data.ext_file),
|
||||||
|
else => error.NotRegularFile,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
fn readerFromData(archive: *Archive, data: anytype) !DataReader {
|
||||||
|
var out: DataReader = .init(archive, data.block_sizes, data.block_start, data.size);
|
||||||
|
if (data.frag_idx != 0xFFFFFFFF)
|
||||||
|
out.addFragment(try archive.frag(data.frag_idx), data.frag_block_offset);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the directory entries for a directory inode.
|
||||||
|
pub fn dirEntries(self: Inode, archive: *Archive) ![]DirEntry {
|
||||||
|
return switch (self.hdr.inode_type) {
|
||||||
|
.dir => entriesFromData(archive, self.data.dir),
|
||||||
|
.ext_dir => entriesFromData(archive, self.data.ext_dir),
|
||||||
|
else => error.NotDirectory,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
fn entriesFromData(archive: *Archive, data: anytype) ![]DirEntry {
|
||||||
|
var rdr = try archive.fil.readerAt(archive.super.dir_start + data.block_start, &[0]u8{});
|
||||||
|
const alloc = archive.allocator();
|
||||||
|
var meta: MetadataReader = .init(alloc, &rdr.interface, archive.decomp);
|
||||||
|
try meta.interface.discardAll(data.block_offset);
|
||||||
|
return DirEntry.readDir(alloc, &meta.interface, data.size);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn extractTo(self: Inode, archive: *Archive, path: []const u8, options: ExtractionOptions) !void {
|
||||||
|
switch (self.hdr.inode_type) {
|
||||||
|
.dir, .ext_dir => {
|
||||||
|
// Removing any trailing separators since that's the easiest path forward.
|
||||||
|
if (path[path.len - 1] == '/') return self.extractTo(archive, path[0 .. path.len - 1], options);
|
||||||
|
var alloc = archive.allocator();
|
||||||
|
const entries = try self.dirEntries(archive);
|
||||||
|
defer {
|
||||||
|
for (entries) |entry| entry.deinit(alloc);
|
||||||
|
alloc.free(entries);
|
||||||
|
}
|
||||||
|
for (entries) |entry| {
|
||||||
|
var new_path = try alloc.alloc(u8, path.len + 1 + entry.name.len);
|
||||||
|
@memcpy(new_path[0..path.len], path);
|
||||||
|
@memcpy(new_path[path.len + 1 ..], entry.name);
|
||||||
|
new_path[path.len] = '/';
|
||||||
|
defer alloc.free(new_path);
|
||||||
|
|
||||||
|
var rdr = try archive.fil.readerAt(archive.super.inode_start + entry.block_start, &[0]u8{});
|
||||||
|
var meta: MetadataReader = .init(alloc, &rdr.interface, archive.decomp);
|
||||||
|
try meta.interface.discardAll(entry.block_offset);
|
||||||
|
var inode: Inode = try read(alloc, &meta.interface, archive.super.block_size);
|
||||||
|
defer inode.deinit(alloc);
|
||||||
|
try inode.extractTo(archive, new_path, options);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.file, .ext_file => try self.extractRegFile(archive, path, options),
|
||||||
|
.symlink, .ext_symlink => try self.extractSymlink(path),
|
||||||
|
else => try self.extractDevice(archive, path, options),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const Perms = struct {
|
||||||
|
path: []const u8,
|
||||||
|
owner: u16,
|
||||||
|
perm: u16,
|
||||||
|
mod_time: u32,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn extractToThreaded(inode: Inode, archive: *Archive, path: []const u8, options: ExtractionOptions, threads: usize) !void {
|
||||||
|
_ = archive;
|
||||||
|
_ = path;
|
||||||
|
_ = options;
|
||||||
|
_ = threads;
|
||||||
|
switch (inode.hdr.inode_type) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Extract threadedly the inode to the path.
|
||||||
|
fn extractThread(inode: Inode, archive: *Archive, path: []const u8, options: ExtractionOptions, wg: *WaitGroup, pool: *Pool, perms: ?*std.ArrayList(Perms)) !void {
|
||||||
|
_ = pool;
|
||||||
|
_ = perms;
|
||||||
|
_ = archive;
|
||||||
|
switch (inode.hdr.inode_type) {
|
||||||
|
.dir, .ext_dir => {
|
||||||
|
//TOOD
|
||||||
|
return error.TODO;
|
||||||
|
},
|
||||||
|
.file, .ext_file => {
|
||||||
|
//TOOD
|
||||||
|
return error.TODO;
|
||||||
|
},
|
||||||
|
.symlink, .ext_symlink => {
|
||||||
|
defer wg.finish();
|
||||||
|
try inode.extractSymlink(path);
|
||||||
|
},
|
||||||
|
else => {
|
||||||
|
defer wg.finish();
|
||||||
|
try inode.extractDevice(path, options.ignore_permissions);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Creates and writes the inode file contents to the given path.
|
||||||
|
/// Optionally set owner & permissions.
|
||||||
|
///
|
||||||
|
/// Assumes the inode is a file or ext_file type.
|
||||||
|
fn extractRegFile(self: Inode, archive: *Archive, path: []const u8, options: ExtractionOptions) !void {
|
||||||
|
var fil = try std.fs.cwd().createFile(path, .{});
|
||||||
|
defer fil.close();
|
||||||
|
var buf: [8192]u8 = undefined;
|
||||||
|
var wrt = fil.writer(&buf);
|
||||||
|
var dat_rdr = try self.dataReader(archive);
|
||||||
|
defer dat_rdr.deinit();
|
||||||
|
_ = try dat_rdr.interface.streamRemaining(&wrt.interface);
|
||||||
|
try wrt.interface.flush();
|
||||||
|
// updateTime is in nanoseconds (a billionth of a second). mod_time is in seconds.
|
||||||
|
try fil.updateTimes(self.hdr.mod_time * (10 ^ 9), self.hdr.mod_time * (10 ^ 9));
|
||||||
|
if (!options.ignore_permissions) {
|
||||||
|
try fil.chmod(self.hdr.permissions);
|
||||||
|
try fil.chown(try archive.id(self.hdr.uid_idx), try archive.id(self.hdr.gid_idx));
|
||||||
|
}
|
||||||
|
if (!options.ignore_xattr) {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// TODO: not implemented
|
||||||
|
/// Extract the inode file contents to the given path.
|
||||||
|
/// The extraction will be done threaded using pool for threads and will call wg.finish() when done.
|
||||||
|
///
|
||||||
|
/// Optionally set owner & permissions.
|
||||||
|
/// Assumes the inode is a file or ext_file type.
|
||||||
|
fn extractRegFileThreaded(self: Inode, archive: *Archive, path: []const u8, options: ExtractionOptions, pool: *Pool, wg: *WaitGroup) !void {
|
||||||
|
_ = self;
|
||||||
|
_ = archive;
|
||||||
|
_ = path;
|
||||||
|
_ = options;
|
||||||
|
_ = pool;
|
||||||
|
_ = wg;
|
||||||
|
return error.TODO;
|
||||||
|
}
|
||||||
|
/// Creates the symlink described by the inode.
|
||||||
|
///
|
||||||
|
/// Assumes the inode is a symlink or ext_symlink type.
|
||||||
|
fn extractSymlink(self: Inode, path: []const u8) !void {
|
||||||
|
const target = switch (self.data) {
|
||||||
|
.symlink => |s| s.target,
|
||||||
|
.ext_symlink => |s| s.target,
|
||||||
|
else => unreachable,
|
||||||
|
};
|
||||||
|
try std.fs.cwd().symLink(target, path, .{});
|
||||||
|
}
|
||||||
|
/// Creates the device described by the inode.
|
||||||
|
///
|
||||||
|
/// Optionally set owner & permissions.
|
||||||
|
/// 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 {
|
||||||
|
var mode: u32 = undefined;
|
||||||
|
var dev: u32 = 0;
|
||||||
|
switch (self.data) {
|
||||||
|
.char_dev => |d| {
|
||||||
|
mode = std.posix.S.IFCHR;
|
||||||
|
dev = d.dev;
|
||||||
|
},
|
||||||
|
.ext_char_dev => |d| {
|
||||||
|
mode = std.posix.S.IFCHR;
|
||||||
|
dev = d.dev;
|
||||||
|
},
|
||||||
|
.block_dev => |d| {
|
||||||
|
mode = std.posix.S.IFBLK;
|
||||||
|
dev = d.dev;
|
||||||
|
},
|
||||||
|
.ext_block_dev => |d| {
|
||||||
|
mode = std.posix.S.IFBLK;
|
||||||
|
dev = d.dev;
|
||||||
|
},
|
||||||
|
.fifo, .ext_fifo => mode = std.posix.S.IFIFO,
|
||||||
|
.socket, .ext_socket => mode = std.posix.S.IFSOCK,
|
||||||
|
else => unreachable,
|
||||||
|
}
|
||||||
|
const res: std.os.linux.E = @enumFromInt(std.os.linux.mknod(@ptrCast(path), mode, dev));
|
||||||
|
switch (res) {
|
||||||
|
.SUCCESS => {},
|
||||||
|
.ACCES => return std.fs.Dir.MakeError.AccessDenied,
|
||||||
|
.DQUOT => return std.fs.Dir.MakeError.DiskQuota,
|
||||||
|
.EXIST => return std.fs.Dir.MakeError.PathAlreadyExists,
|
||||||
|
.FAULT, .NOENT => return std.fs.Dir.MakeError.BadPathName,
|
||||||
|
.LOOP => return std.fs.Dir.MakeError.SymLinkLoop,
|
||||||
|
.NAMETOOLONG => return std.fs.Dir.MakeError.NameTooLong,
|
||||||
|
.NOMEM => return std.fs.Dir.MakeError.SystemResources,
|
||||||
|
.NOSPC => return std.fs.Dir.MakeError.NoSpaceLeft,
|
||||||
|
.NOTDIR => return std.fs.Dir.MakeError.NotDir,
|
||||||
|
.PERM => return std.fs.Dir.MakeError.PermissionDenied,
|
||||||
|
.ROFS => return std.fs.Dir.MakeError.ReadOnlyFileSystem,
|
||||||
|
else => return blk: {
|
||||||
|
std.debug.print("unhandled mknod result: {}\n", .{res});
|
||||||
|
break :blk std.fs.Dir.MakeError.Unexpected;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
var fil = try std.fs.cwd().openFile(path, .{});
|
||||||
|
// updateTime is in nanoseconds (a billionth of a second). mod_time is in seconds.
|
||||||
|
try fil.updateTimes(self.hdr.mod_time * (10 ^ 9), self.hdr.mod_time * (10 ^ 9));
|
||||||
|
if (!options.ignore_permissions) {
|
||||||
|
try fil.chmod(self.hdr.permissions);
|
||||||
|
try fil.chown(try archive.id(self.hdr.uid_idx), try archive.id(self.hdr.gid_idx));
|
||||||
|
}
|
||||||
|
if (!options.ignore_xattr) {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+15
-10
@@ -5,16 +5,21 @@ const Writer = std.Io.Writer;
|
|||||||
|
|
||||||
const ExtractionOptions = @This();
|
const ExtractionOptions = @This();
|
||||||
|
|
||||||
/// Don't set the file's permissions after extraction
|
/// Don't set the file's owner & permissions after extraction
|
||||||
ignorePermissions: bool = false,
|
ignore_permissions: bool = false,
|
||||||
/// Don't set the file's owner after extraction.
|
/// Don't set xattr values. Currently xattrs are never set anyway.
|
||||||
ignoreOwner: bool = false,
|
ignore_xattr: bool = false,
|
||||||
/// Replace symlinks with their target.
|
/// Replace symlinks with their target.
|
||||||
dereferenceSymlinks: bool = false,
|
dereference_symlinks: bool = false,
|
||||||
|
/// Verbose logging. If true, verbose_writer must be set
|
||||||
log_level: std.log.Level = .err,
|
verbose: bool = false,
|
||||||
// /// If options verbose and verboseWriter not set, logs are printed to stdout.
|
/// Where to print verbose log.
|
||||||
// verboseWriter: ?*Writer = null,
|
verbose_writer: ?*Writer = null,
|
||||||
|
|
||||||
pub const Default: ExtractionOptions = .{};
|
pub const Default: ExtractionOptions = .{};
|
||||||
pub const VerboseDefault: ExtractionOptions = .{ .log_level = .debug };
|
pub fn VerboseDefault(wrt: *Writer) ExtractionOptions {
|
||||||
|
return .{
|
||||||
|
.verbose = true,
|
||||||
|
.verbose_writer = wrt,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
+3
-3
@@ -1,7 +1,7 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const Mutex = std.Thread.Mutex;
|
const Mutex = std.Thread.Mutex;
|
||||||
|
|
||||||
const DecompMgr = @import("decomp.zig");
|
const DecompFn = @import("decomp.zig").DecompFn;
|
||||||
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");
|
||||||
|
|
||||||
@@ -18,7 +18,7 @@ pub fn Table(T: anytype) type {
|
|||||||
|
|
||||||
alloc: std.mem.Allocator,
|
alloc: std.mem.Allocator,
|
||||||
fil: OffsetFile,
|
fil: OffsetFile,
|
||||||
decomp: *DecompMgr,
|
decomp: DecompFn,
|
||||||
tab_start: u64,
|
tab_start: u64,
|
||||||
|
|
||||||
tab: std.AutoHashMap(u32, []T),
|
tab: std.AutoHashMap(u32, []T),
|
||||||
@@ -26,7 +26,7 @@ pub fn Table(T: anytype) type {
|
|||||||
|
|
||||||
mut: Mutex = .{},
|
mut: Mutex = .{},
|
||||||
|
|
||||||
pub fn init(alloc: std.mem.Allocator, fil: OffsetFile, decomp: *DecompMgr, tab_start: u64, values: u32) !Self {
|
pub fn init(alloc: std.mem.Allocator, fil: OffsetFile, decomp: DecompFn, tab_start: u64, values: u32) !Self {
|
||||||
return .{
|
return .{
|
||||||
.alloc = alloc,
|
.alloc = alloc,
|
||||||
.fil = fil,
|
.fil = fil,
|
||||||
|
|||||||
+8
-8
@@ -7,7 +7,7 @@ const Limit = std.Io.Limit;
|
|||||||
|
|
||||||
const Archive = @import("../archive.zig");
|
const Archive = @import("../archive.zig");
|
||||||
const FragEntry = Archive.FragEntry;
|
const FragEntry = Archive.FragEntry;
|
||||||
const DecompMgr = @import("../decomp.zig");
|
const DecompFn = @import("../decomp.zig").DecompFn;
|
||||||
const BlockSize = @import("../inode_data/file.zig").BlockSize;
|
const BlockSize = @import("../inode_data/file.zig").BlockSize;
|
||||||
const OffsetFile = @import("offset_file.zig");
|
const OffsetFile = @import("offset_file.zig");
|
||||||
|
|
||||||
@@ -15,7 +15,7 @@ const DataReader = @This();
|
|||||||
|
|
||||||
alloc: std.mem.Allocator,
|
alloc: std.mem.Allocator,
|
||||||
fil: OffsetFile,
|
fil: OffsetFile,
|
||||||
decomp: *DecompMgr,
|
decomp: DecompFn,
|
||||||
block_size: u32,
|
block_size: u32,
|
||||||
|
|
||||||
blocks: []BlockSize,
|
blocks: []BlockSize,
|
||||||
@@ -33,7 +33,7 @@ pub fn init(archive: *Archive, blocks: []BlockSize, start: u64, size: u64) DataR
|
|||||||
return .{
|
return .{
|
||||||
.alloc = archive.allocator(),
|
.alloc = archive.allocator(),
|
||||||
.fil = archive.fil,
|
.fil = archive.fil,
|
||||||
.decomp = &archive.decomp,
|
.decomp = archive.decomp,
|
||||||
.block_size = archive.super.block_size,
|
.block_size = archive.super.block_size,
|
||||||
.blocks = blocks,
|
.blocks = blocks,
|
||||||
.size = size,
|
.size = size,
|
||||||
@@ -91,10 +91,10 @@ fn advance(self: *DataReader) !void {
|
|||||||
}
|
}
|
||||||
const tmp_buf = try self.alloc.alloc(u8, self.frag.?.size.size);
|
const tmp_buf = try self.alloc.alloc(u8, self.frag.?.size.size);
|
||||||
defer self.alloc.free(tmp_buf);
|
defer self.alloc.free(tmp_buf);
|
||||||
var limit_rdr = Reader.limited(&rdr.interface, @enumFromInt(self.frag.?.size.size), tmp_buf);
|
try rdr.interface.readSliceAll(tmp_buf);
|
||||||
const needed_block = try self.alloc.alloc(u8, self.frag_offset + cur_block_size);
|
const needed_block = try self.alloc.alloc(u8, self.frag_offset + cur_block_size);
|
||||||
defer self.alloc.free(needed_block);
|
defer self.alloc.free(needed_block);
|
||||||
_ = try self.decomp.decompReader(&limit_rdr.interface, needed_block);
|
_ = try self.decomp(self.alloc, tmp_buf, needed_block);
|
||||||
@memcpy(self.interface.buffer, needed_block[self.frag_offset..]);
|
@memcpy(self.interface.buffer, needed_block[self.frag_offset..]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -109,9 +109,9 @@ fn advance(self: *DataReader) !void {
|
|||||||
try rdr.interface.readSliceAll(self.interface.buffer);
|
try rdr.interface.readSliceAll(self.interface.buffer);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var buf: [8192]u8 = undefined; //TODO: possibly change for better performance/memory usage. Might need to be a full block in size.
|
const tmp_buf = try self.alloc.alloc(u8, block.size);
|
||||||
var limit_rdr = Reader.limited(&rdr.interface, @enumFromInt(block.size), &buf);
|
defer self.alloc.free(tmp_buf);
|
||||||
_ = try self.decomp.decompReader(&limit_rdr.interface, self.interface.buffer);
|
_ = try self.decomp(self.alloc, tmp_buf, self.interface.buffer);
|
||||||
}
|
}
|
||||||
/// Does not guarentee that data currently in the buffer is retained.
|
/// Does not guarentee that data currently in the buffer is retained.
|
||||||
fn resizeBuffer(self: *DataReader, size: usize) !void {
|
fn resizeBuffer(self: *DataReader, size: usize) !void {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ const Writer = std.Io.Writer;
|
|||||||
const Limit = std.Io.Limit;
|
const Limit = std.Io.Limit;
|
||||||
const StreamError = std.Io.Reader.StreamError;
|
const StreamError = std.Io.Reader.StreamError;
|
||||||
|
|
||||||
const DecompMgr = @import("../decomp.zig");
|
const DecompFn = @import("../decomp.zig").DecompFn;
|
||||||
|
|
||||||
const BlockHeader = packed struct {
|
const BlockHeader = packed struct {
|
||||||
size: u15,
|
size: u15,
|
||||||
@@ -15,14 +15,14 @@ const This = @This();
|
|||||||
|
|
||||||
alloc: std.mem.Allocator,
|
alloc: std.mem.Allocator,
|
||||||
rdr: *Reader,
|
rdr: *Reader,
|
||||||
decomp: *DecompMgr,
|
decomp: DecompFn,
|
||||||
|
|
||||||
buf: [8192]u8 = undefined,
|
buf: [8192]u8 = undefined,
|
||||||
|
|
||||||
interface: Reader,
|
interface: Reader,
|
||||||
err: ?anyerror = null,
|
err: ?anyerror = null,
|
||||||
|
|
||||||
pub fn init(alloc: std.mem.Allocator, rdr: *Reader, decomp: *DecompMgr) This {
|
pub fn init(alloc: std.mem.Allocator, rdr: *Reader, decomp: DecompFn) This {
|
||||||
return .{
|
return .{
|
||||||
.alloc = alloc,
|
.alloc = alloc,
|
||||||
.rdr = rdr,
|
.rdr = rdr,
|
||||||
@@ -51,8 +51,8 @@ fn advance(self: *This) !void {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var tmp_buf: [8192]u8 = undefined;
|
var tmp_buf: [8192]u8 = undefined;
|
||||||
var limit_rdr = self.rdr.limited(@enumFromInt(hdr.size), &tmp_buf);
|
try self.rdr.readSliceAll(tmp_buf[0..hdr.size]);
|
||||||
self.interface.end = try self.decomp.decompReader(&limit_rdr.interface, &self.buf);
|
self.interface.end = try self.decomp(self.alloc, tmp_buf[0..hdr.size], &self.buf);
|
||||||
self.interface.buffer = self.buf[0..self.interface.end];
|
self.interface.buffer = self.buf[0..self.interface.end];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user