Added --force to unsquashfs
Fixing race condition bugs (yay)
This commit is contained in:
@@ -0,0 +1,30 @@
|
|||||||
|
// Project-local debug tasks
|
||||||
|
//
|
||||||
|
// For more documentation on how to configure debug tasks,
|
||||||
|
// see: https://zed.dev/docs/debugger
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"label": "Build & Run",
|
||||||
|
|
||||||
|
"adapter": "CodeLLDB",
|
||||||
|
"request": "launch",
|
||||||
|
|
||||||
|
"build": {
|
||||||
|
"command": "zig",
|
||||||
|
"args": [
|
||||||
|
"build",
|
||||||
|
"-Doptimize=Debug",
|
||||||
|
"-Duse_c_libs=true",
|
||||||
|
"-Dvalgrind=true",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
"program": "zig-out/bin/unsquashfs",
|
||||||
|
"args": [
|
||||||
|
"--force",
|
||||||
|
"-d",
|
||||||
|
"testing/TestExtractUnsquashfs",
|
||||||
|
"testing/LinuxPATest.sfs",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]
|
||||||
@@ -52,6 +52,7 @@ pub fn build(b: *std.Build) !void {
|
|||||||
const exe = b.addExecutable(.{
|
const exe = b.addExecutable(.{
|
||||||
.name = "unsquashfs",
|
.name = "unsquashfs",
|
||||||
.root_module = exe_mod,
|
.root_module = exe_mod,
|
||||||
|
.use_llvm = true,
|
||||||
});
|
});
|
||||||
|
|
||||||
const lib = b.addLibrary(.{
|
const lib = b.addLibrary(.{
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ const help_mgs =
|
|||||||
\\ -p <threads> Specify how many threads to use. If no present or zero, the system's logical cores count is used.
|
\\ -p <threads> Specify how many threads to use. If no present or zero, the system's logical cores count is used.
|
||||||
\\ -v Verbose
|
\\ -v Verbose
|
||||||
\\
|
\\
|
||||||
|
\\ --force Force extraction. If the destination already exists, it will be deleted.
|
||||||
|
\\
|
||||||
\\ --help Display this messages
|
\\ --help Display this messages
|
||||||
\\ --version Display the version
|
\\ --version Display the version
|
||||||
\\
|
\\
|
||||||
@@ -34,6 +36,7 @@ var threads: u32 = 0;
|
|||||||
var verbose: bool = false;
|
var verbose: bool = false;
|
||||||
var ignore_xattrs: bool = false;
|
var ignore_xattrs: bool = false;
|
||||||
var ignore_permissions: bool = false;
|
var ignore_permissions: bool = false;
|
||||||
|
var force: bool = false;
|
||||||
|
|
||||||
pub fn main() !void {
|
pub fn main() !void {
|
||||||
const alloc = std.heap.smp_allocator;
|
const alloc = std.heap.smp_allocator;
|
||||||
@@ -57,6 +60,8 @@ pub fn main() !void {
|
|||||||
.ignore_xattr = ignore_xattrs,
|
.ignore_xattr = ignore_xattrs,
|
||||||
.ignore_permissions = ignore_permissions,
|
.ignore_permissions = ignore_permissions,
|
||||||
};
|
};
|
||||||
|
if (force)
|
||||||
|
try std.fs.cwd().deleteTree(extLoc);
|
||||||
try arc.extract(alloc, extLoc, options); //TODO: Handle error gracefully.
|
try arc.extract(alloc, extLoc, options); //TODO: Handle error gracefully.
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,6 +109,9 @@ fn handleArgs(alloc: std.mem.Allocator, out: *Writer) !void {
|
|||||||
} else if (std.mem.eql(u8, arg, "-dp")) {
|
} else if (std.mem.eql(u8, arg, "-dp")) {
|
||||||
ignore_permissions = true;
|
ignore_permissions = true;
|
||||||
continue;
|
continue;
|
||||||
|
} else if (std.mem.eql(u8, arg, "--force")) {
|
||||||
|
force = true;
|
||||||
|
continue;
|
||||||
} else if (std.mem.eql(u8, arg, "--version")) {
|
} else if (std.mem.eql(u8, arg, "--version")) {
|
||||||
try out.print("zig-unsquashfs v", .{});
|
try out.print("zig-unsquashfs v", .{});
|
||||||
try config.version.format(out);
|
try config.version.format(out);
|
||||||
|
|||||||
@@ -53,18 +53,26 @@ pub fn addFragment(self: *ThreadedDataReader, entry: FragEntry, frag_offset: u32
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Extract the data to the file threadedly, using pool to spawn threads.
|
/// Extract the data to the file threadedly, using pool to spawn threads.
|
||||||
/// This function only returns an error if pool.spawn fails. For actual extraction errors finish.out_err will be set.
|
/// If errors occur, they are set to finish.out_err.
|
||||||
pub fn extractThreaded(self: ThreadedDataReader, file: std.fs.File, pool: *Pool, finish: *InodeFinish) !void {
|
pub fn extractThreaded(self: ThreadedDataReader, file: std.fs.File, pool: *Pool, finish: *InodeFinish) void {
|
||||||
var cur_write_offset: u64 = 0;
|
var cur_write_offset: u64 = 0;
|
||||||
var cur_read_offset: u64 = self.start_offset;
|
var cur_read_offset: u64 = self.start_offset;
|
||||||
for (0..self.blocks.len) |i| {
|
for (0..self.blocks.len) |i| {
|
||||||
const cur_block_size = if (i == self.num_blocks - 1) self.size % self.block_size else self.block_size;
|
const cur_block_size = if (i == self.num_blocks - 1) self.size % self.block_size else self.block_size;
|
||||||
try pool.spawn(workThreadBlocks, .{ self, file, cur_write_offset, cur_read_offset, self.blocks[i], cur_block_size, finish });
|
pool.spawn(workThreadBlocks, .{ self, file, cur_write_offset, cur_read_offset, self.blocks[i], cur_block_size, finish }) catch |res_err| {
|
||||||
|
finish.logError("Can't spawn pool task: {}", .{res_err});
|
||||||
|
finish.out_err.* = res_err;
|
||||||
|
finish.finish();
|
||||||
|
};
|
||||||
cur_write_offset += cur_block_size;
|
cur_write_offset += cur_block_size;
|
||||||
cur_read_offset += self.blocks[i].size;
|
cur_read_offset += self.blocks[i].size;
|
||||||
}
|
}
|
||||||
if (self.frag != null)
|
if (self.frag != null)
|
||||||
try pool.spawn(workThreadFragment, .{ self, file, cur_write_offset, finish });
|
pool.spawn(workThreadFragment, .{ self, file, cur_write_offset, finish }) catch |res_err| {
|
||||||
|
finish.logError("Can't spawn pool task: {}", .{res_err});
|
||||||
|
finish.out_err.* = res_err;
|
||||||
|
finish.finish();
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn workThreadBlocks(
|
fn workThreadBlocks(
|
||||||
@@ -79,25 +87,30 @@ fn workThreadBlocks(
|
|||||||
defer finish.finish();
|
defer finish.finish();
|
||||||
var wrt = fil.writer(&[0]u8{});
|
var wrt = fil.writer(&[0]u8{});
|
||||||
wrt.seekTo(write_offset) catch |err| {
|
wrt.seekTo(write_offset) catch |err| {
|
||||||
|
finish.logError("Error seeking file writer: {}", .{err});
|
||||||
finish.out_err.* = err;
|
finish.out_err.* = err;
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
defer wrt.interface.flush() catch |err| {
|
defer wrt.interface.flush() catch |err| {
|
||||||
|
finish.logError("Error flushing file writer: {}", .{err});
|
||||||
finish.out_err.* = err;
|
finish.out_err.* = err;
|
||||||
};
|
};
|
||||||
if (block.size == 0) {
|
if (block.size == 0) {
|
||||||
wrt.interface.splatByteAll(0, cur_block_size) catch |err| {
|
wrt.interface.splatByteAll(0, cur_block_size) catch |err| {
|
||||||
|
finish.logError("Error writing zeroes: {}", .{err});
|
||||||
finish.out_err.* = err;
|
finish.out_err.* = err;
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var rdr = self.fil.readerAt(read_offset, &[0]u8{}) catch |err| {
|
var rdr = self.fil.readerAt(read_offset, &[0]u8{}) catch |err| {
|
||||||
|
finish.logError("Error creating file reader: {}", .{err});
|
||||||
finish.out_err.* = err;
|
finish.out_err.* = err;
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if (block.uncompressed) {
|
if (block.uncompressed) {
|
||||||
rdr.interface.streamExact(&wrt.interface, block.size) catch |err| {
|
rdr.interface.streamExact(&wrt.interface, block.size) catch |err| {
|
||||||
|
finish.logError("Error streaming data: {}", .{err});
|
||||||
finish.out_err.* = err;
|
finish.out_err.* = err;
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
@@ -105,25 +118,30 @@ fn workThreadBlocks(
|
|||||||
}
|
}
|
||||||
// TODO: shared buffers
|
// TODO: shared buffers
|
||||||
const read_buf = self.alloc.alloc(u8, block.size) catch |err| {
|
const read_buf = self.alloc.alloc(u8, block.size) catch |err| {
|
||||||
|
finish.logError("Error creating reader buffer: {}", .{err});
|
||||||
finish.out_err.* = err;
|
finish.out_err.* = err;
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
defer self.alloc.free(read_buf);
|
defer self.alloc.free(read_buf);
|
||||||
rdr.interface.readSliceAll(read_buf) catch |err| {
|
rdr.interface.readSliceAll(read_buf) catch |err| {
|
||||||
|
finish.logError("Error reading data into reader buffer: {}", .{err});
|
||||||
finish.out_err.* = err;
|
finish.out_err.* = err;
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
// TODO: shared buffers
|
// TODO: shared buffers
|
||||||
const res_buf = self.alloc.alloc(u8, cur_block_size) catch |err| {
|
const res_buf = self.alloc.alloc(u8, cur_block_size) catch |err| {
|
||||||
|
finish.logError("Error creating result buffer: {}", .{err});
|
||||||
finish.out_err.* = err;
|
finish.out_err.* = err;
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
defer self.alloc.free(res_buf);
|
defer self.alloc.free(res_buf);
|
||||||
_ = self.decomp(self.alloc, read_buf, res_buf) catch |err| {
|
_ = self.decomp(self.alloc, read_buf, res_buf) catch |err| {
|
||||||
|
finish.logError("Error decompressing data block: {}", .{err});
|
||||||
finish.out_err.* = err;
|
finish.out_err.* = err;
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
wrt.interface.writeAll(res_buf) catch |err| {
|
wrt.interface.writeAll(res_buf) catch |err| {
|
||||||
|
finish.logError("Error writing to file: {}", .{err});
|
||||||
finish.out_err.* = err;
|
finish.out_err.* = err;
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
@@ -133,6 +151,7 @@ fn workThreadFragment(self: ThreadedDataReader, fil: std.fs.File, write_offset:
|
|||||||
|
|
||||||
var wrt = fil.writer(&[0]u8{});
|
var wrt = fil.writer(&[0]u8{});
|
||||||
wrt.seekTo(write_offset) catch |err| {
|
wrt.seekTo(write_offset) catch |err| {
|
||||||
|
finish.logError("Error seeking file writer for file fragment: {}", .{err});
|
||||||
finish.out_err.* = err;
|
finish.out_err.* = err;
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
@@ -141,39 +160,47 @@ fn workThreadFragment(self: ThreadedDataReader, fil: std.fs.File, write_offset:
|
|||||||
};
|
};
|
||||||
|
|
||||||
var rdr = self.fil.readerAt(self.frag.?.start, &[0]u8{}) catch |err| {
|
var rdr = self.fil.readerAt(self.frag.?.start, &[0]u8{}) catch |err| {
|
||||||
|
finish.logError("Error creating file reader for file fragment: {}", .{err});
|
||||||
finish.out_err.* = err;
|
finish.out_err.* = err;
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if (self.frag.?.size.uncompressed) {
|
if (self.frag.?.size.uncompressed) {
|
||||||
rdr.interface.discardAll(self.frag_offset) catch |err| {
|
rdr.interface.discardAll(self.frag_offset) catch |err| {
|
||||||
|
finish.logError("Error discarding useless fragment data: {}", .{err});
|
||||||
finish.out_err.* = err;
|
finish.out_err.* = err;
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
rdr.interface.streamExact(&wrt.interface, self.size % self.block_size) catch |err| {
|
rdr.interface.streamExact(&wrt.interface, self.size % self.block_size) catch |err| {
|
||||||
|
finish.logError("Error streaming fragment data: {}", .{err});
|
||||||
finish.out_err.* = err;
|
finish.out_err.* = err;
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const tmp_buf = self.alloc.alloc(u8, self.frag.?.size.size) catch |err| {
|
const tmp_buf = self.alloc.alloc(u8, self.frag.?.size.size) catch |err| {
|
||||||
|
finish.logError("Error creating a temporary buffer for a file fragment: {}", .{err});
|
||||||
finish.out_err.* = err;
|
finish.out_err.* = err;
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
defer self.alloc.free(tmp_buf);
|
defer self.alloc.free(tmp_buf);
|
||||||
rdr.interface.readSliceAll(tmp_buf) catch |err| {
|
rdr.interface.readSliceAll(tmp_buf) catch |err| {
|
||||||
|
finish.logError("Error reading data into fragment buffer: {}", .{err});
|
||||||
finish.out_err.* = err;
|
finish.out_err.* = err;
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
const needed_block = self.alloc.alloc(u8, self.block_size) catch |err| {
|
const needed_block = self.alloc.alloc(u8, self.block_size) catch |err| {
|
||||||
|
finish.logError("Error allocating fragment decompression results: {}", .{err});
|
||||||
finish.out_err.* = err;
|
finish.out_err.* = err;
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
defer self.alloc.free(needed_block);
|
defer self.alloc.free(needed_block);
|
||||||
_ = self.decomp(self.alloc, tmp_buf, needed_block) catch |err| {
|
_ = self.decomp(self.alloc, tmp_buf, needed_block) catch |err| {
|
||||||
|
finish.logError("Error decompressing fragment: {}", .{err});
|
||||||
finish.out_err.* = err;
|
finish.out_err.* = err;
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
wrt.interface.writeAll(needed_block[self.frag_offset .. self.frag_offset + (self.size % self.block_size)]) catch |err| {
|
wrt.interface.writeAll(needed_block[self.frag_offset .. self.frag_offset + (self.size % self.block_size)]) catch |err| {
|
||||||
|
finish.logError("Error writing fragment: {}", .{err});
|
||||||
finish.out_err.* = err;
|
finish.out_err.* = err;
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|||||||
+37
-18
@@ -28,7 +28,7 @@ pub fn extractTo(
|
|||||||
var arena: std.heap.ArenaAllocator = .init(stack_alloc.get());
|
var arena: std.heap.ArenaAllocator = .init(stack_alloc.get());
|
||||||
defer arena.deinit();
|
defer arena.deinit();
|
||||||
if (options.threads <= 1)
|
if (options.threads <= 1)
|
||||||
return extractToSingle(arena.allocator(), inode, archive, path, options);
|
return extractSingleThread(arena.allocator(), inode, archive, path, options);
|
||||||
|
|
||||||
var thread_alloc = std.heap.ThreadSafeAllocator{ .child_allocator = arena.allocator() };
|
var thread_alloc = std.heap.ThreadSafeAllocator{ .child_allocator = arena.allocator() };
|
||||||
const alloc = thread_alloc.allocator();
|
const alloc = thread_alloc.allocator();
|
||||||
@@ -39,21 +39,22 @@ pub fn extractTo(
|
|||||||
|
|
||||||
var wg: WaitGroup = .{};
|
var wg: WaitGroup = .{};
|
||||||
var err: ?anyerror = null;
|
var err: ?anyerror = null;
|
||||||
extractToMulti(
|
wg.start();
|
||||||
|
try pool.spawn(extractMultiThread, .{
|
||||||
alloc,
|
alloc,
|
||||||
inode,
|
inode,
|
||||||
archive,
|
archive,
|
||||||
path,
|
path,
|
||||||
options,
|
options,
|
||||||
&pool,
|
&pool,
|
||||||
.{ .wg = &wg },
|
FinishUnion{ .wg = &wg },
|
||||||
&err,
|
&err,
|
||||||
);
|
});
|
||||||
pool.waitAndWork(&wg);
|
pool.waitAndWork(&wg);
|
||||||
if (err != null) return err.?;
|
if (err != null) return err.?;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extractToSingle(
|
fn extractSingleThread(
|
||||||
alloc: Allocator,
|
alloc: Allocator,
|
||||||
inode: Inode,
|
inode: Inode,
|
||||||
archive: *Archive,
|
archive: *Archive,
|
||||||
@@ -72,14 +73,14 @@ fn extractToSingle(
|
|||||||
// otherwise be more conscientious about freeing memory.
|
// otherwise be more conscientious about freeing memory.
|
||||||
// For now, this is good enough.
|
// For now, this is good enough.
|
||||||
|
|
||||||
const entries = try inode.dirEntries(alloc, archive);
|
const entries = try inode.dirEntries(alloc, archive.*);
|
||||||
for (entries) |ent| {
|
for (entries) |ent| {
|
||||||
const sub_inode: Inode = try .readFromEntry(alloc, archive, ent);
|
const sub_inode: Inode = try .readFromEntry(alloc, archive, ent);
|
||||||
const new_path = try std.mem.concat(alloc, u8, []const []const u8{ path, "/", ent.name });
|
const new_path = try std.mem.concat(alloc, u8, &[_][]const u8{ path, "/", ent.name });
|
||||||
extractToSingle(alloc, sub_inode, archive, new_path, options);
|
try extractSingleThread(alloc, sub_inode, archive, new_path, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
const fil = try std.fs.cwd().openFile(path);
|
const fil = try std.fs.cwd().openFile(path, .{});
|
||||||
defer fil.close();
|
defer fil.close();
|
||||||
try inode.setMetadata(alloc, archive, fil, options);
|
try inode.setMetadata(alloc, archive, fil, options);
|
||||||
},
|
},
|
||||||
@@ -99,7 +100,7 @@ fn extractToSingle(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extractToMulti(
|
fn extractMultiThread(
|
||||||
alloc: Allocator,
|
alloc: Allocator,
|
||||||
inode: Inode,
|
inode: Inode,
|
||||||
archive: *Archive,
|
archive: *Archive,
|
||||||
@@ -109,7 +110,7 @@ fn extractToMulti(
|
|||||||
fin: FinishUnion,
|
fin: FinishUnion,
|
||||||
err: *?anyerror,
|
err: *?anyerror,
|
||||||
) void {
|
) void {
|
||||||
if (err != null) {
|
if (err.* != null) {
|
||||||
fin.finish();
|
fin.finish();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -129,7 +130,7 @@ fn extractToMulti(
|
|||||||
// otherwise be more conscientious about freeing memory.
|
// otherwise be more conscientious about freeing memory.
|
||||||
// For now, this is good enough.
|
// For now, this is good enough.
|
||||||
|
|
||||||
const entries = try inode.dirEntries(alloc, archive) catch |res_err| {
|
const entries = inode.dirEntries(alloc, archive.*) catch |res_err| {
|
||||||
err.* = res_err;
|
err.* = res_err;
|
||||||
fin.finish();
|
fin.finish();
|
||||||
return;
|
return;
|
||||||
@@ -153,9 +154,21 @@ fn extractToMulti(
|
|||||||
|
|
||||||
for (entries) |ent| {
|
for (entries) |ent| {
|
||||||
if (ent.inode_type == .dir)
|
if (ent.inode_type == .dir)
|
||||||
extractEntry(alloc, ent, archive, path, options, pool, dir_fin, err);
|
extractEntry(
|
||||||
|
alloc,
|
||||||
|
ent,
|
||||||
|
archive,
|
||||||
|
path,
|
||||||
|
options,
|
||||||
|
pool,
|
||||||
|
.{ .fin = dir_fin },
|
||||||
|
err,
|
||||||
|
);
|
||||||
|
|
||||||
pool.spawn(extractEntry, .{ alloc, ent, archive, path, options, pool, dir_fin, err }) catch |res_err| {
|
pool.spawn(
|
||||||
|
extractEntry,
|
||||||
|
.{ alloc, ent, archive, path, options, pool, FinishUnion{ .fin = dir_fin }, err },
|
||||||
|
) catch |res_err| {
|
||||||
err.* = res_err;
|
err.* = res_err;
|
||||||
dir_fin.finish();
|
dir_fin.finish();
|
||||||
return;
|
return;
|
||||||
@@ -164,12 +177,16 @@ fn extractToMulti(
|
|||||||
},
|
},
|
||||||
.file, .ext_file => {
|
.file, .ext_file => {
|
||||||
const fil = std.fs.cwd().createFile(path, .{ .exclusive = true }) catch |res_err| {
|
const fil = std.fs.cwd().createFile(path, .{ .exclusive = true }) catch |res_err| {
|
||||||
|
if (options.verbose)
|
||||||
|
options.verbose_writer.?.print("Can't create file at {s}: {}\n", .{ path, res_err }) catch {};
|
||||||
err.* = res_err;
|
err.* = res_err;
|
||||||
fin.finish();
|
fin.finish();
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
var data_rdr = threadedDataReader(inode, alloc, archive) catch |res_err| {
|
var data_rdr = threadedDataReader(inode, alloc, archive) catch |res_err| {
|
||||||
|
if (options.verbose)
|
||||||
|
options.verbose_writer.?.print("Can't create data reader for inode #{} (extracting to {s}): {}\n", .{ inode.hdr.num, path, res_err }) catch {};
|
||||||
err.* = res_err;
|
err.* = res_err;
|
||||||
fin.finish();
|
fin.finish();
|
||||||
return;
|
return;
|
||||||
@@ -185,6 +202,8 @@ fn extractToMulti(
|
|||||||
fil,
|
fil,
|
||||||
data_rdr.num_blocks,
|
data_rdr.num_blocks,
|
||||||
) catch |res_err| {
|
) catch |res_err| {
|
||||||
|
if (options.verbose)
|
||||||
|
options.verbose_writer.?.print("Can't create callback for inode #{} (extracting to {s}): {}\n", .{ inode.hdr.num, path, res_err }) catch {};
|
||||||
err.* = res_err;
|
err.* = res_err;
|
||||||
fin.finish();
|
fin.finish();
|
||||||
return;
|
return;
|
||||||
@@ -207,7 +226,7 @@ fn extractToMulti(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fn extractEntry(
|
fn extractEntry(
|
||||||
alloc: Allocator,
|
alloc: Allocator,
|
||||||
ent: DirEntry,
|
ent: DirEntry,
|
||||||
archive: *Archive,
|
archive: *Archive,
|
||||||
@@ -217,18 +236,18 @@ inline fn extractEntry(
|
|||||||
fin: FinishUnion,
|
fin: FinishUnion,
|
||||||
err: *?anyerror,
|
err: *?anyerror,
|
||||||
) void {
|
) void {
|
||||||
const new_path = std.mem.concat(alloc, u8, []const []const u8{ path, "/", ent.name }) catch |res_err| {
|
const new_path = std.mem.concat(alloc, u8, &[_][]const u8{ path, "/", ent.name }) catch |res_err| {
|
||||||
err.* = res_err;
|
err.* = res_err;
|
||||||
fin.finish();
|
fin.finish();
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
const inode: Inode = .readFromEntry(alloc, archive, ent) catch |res_err| {
|
const inode = Inode.readFromEntry(alloc, archive, ent) catch |res_err| {
|
||||||
err.* = res_err;
|
err.* = res_err;
|
||||||
fin.finish();
|
fin.finish();
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
extractToMulti(alloc, inode, archive, new_path, options, pool, fin, err);
|
extractMultiThread(alloc, inode, archive, new_path, options, pool, fin, err);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a threaded data reader for a file inode.
|
/// Get a threaded data reader for a file inode.
|
||||||
|
|||||||
@@ -65,6 +65,11 @@ pub fn create(
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn logError(self: *InodeFinish, comptime fmt: []const u8, args: anytype) void {
|
||||||
|
if (self.options.verbose)
|
||||||
|
self.options.verbose_writer.?.print(fmt, args) catch {};
|
||||||
|
}
|
||||||
|
|
||||||
pub fn finish(self: *InodeFinish) void {
|
pub fn finish(self: *InodeFinish) void {
|
||||||
self.mut.lock();
|
self.mut.lock();
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user