Further progress on extraction

This commit is contained in:
Caleb Gardner
2025-07-18 22:30:30 -05:00
parent de988f083f
commit eb214feefa
3 changed files with 116 additions and 29 deletions
+1 -1
View File
@@ -12,7 +12,7 @@ ignore_permissions: bool = false,
/// Verbose logging /// Verbose logging
verbose: bool = false, verbose: bool = false,
/// Verbose logging writer. If not set, stdout is used. /// Verbose logging writer. If not set, stdout is used.
verbose_logger: ?std.io.AnyWriter = null, verbose_logger: std.io.AnyWriter = std.io.getStdOut().writer().any(),
/// Number of threads used during extraction. Defualts to std.Thread.getCpuCount(). /// Number of threads used during extraction. Defualts to std.Thread.getCpuCount().
thread_count: usize, thread_count: usize,
+114 -27
View File
@@ -185,13 +185,11 @@ pub fn File(comptime T: type) type {
const WaitGroup = std.Thread.WaitGroup; const WaitGroup = std.Thread.WaitGroup;
const Pool = std.Thread.Pool; const Pool = std.Thread.Pool;
const Mutex = std.Thread.Mutex;
pub const ExtractError = error{FileExists}; pub const ExtractError = error{FileExists};
pub fn extract(self: Self, op: *ExtractionOptions, path: []const u8) !void { pub fn extract(self: Self, op: ExtractionOptions, path: []const u8) !void {
if (op.verbose and op.verbose_logger == null) {
op.verbose_logger = std.io.getStdOut().writer().any();
}
var exists = true; var exists = true;
var stat: ?std.fs.File.Stat = null; var stat: ?std.fs.File.Stat = null;
if (std.fs.cwd().statFile(path)) |s| { if (std.fs.cwd().statFile(path)) |s| {
@@ -222,33 +220,43 @@ pub fn File(comptime T: type) type {
defer pol.deinit(); defer pol.deinit();
var errs: std.ArrayList(anyerror) = .init(self.rdr.alloc); var errs: std.ArrayList(anyerror) = .init(self.rdr.alloc);
defer errs.deinit(); defer errs.deinit();
try self.extractReal(op, &errs, &wg, &pol, path); try self.extractReal(op, &errs, &wg, &pol, path, true);
wg.wait(); wg.wait();
if (errs.items.len > 0) return errs.items[0]; if (errs.items.len > 0) return errs.items[0];
} }
fn extractReal( fn extractReal(
self: Self, self: Self,
op: *ExtractionOptions, op: ExtractionOptions,
errs: *std.ArrayList(anyerror), errs: *std.ArrayList(anyerror),
wg: *WaitGroup, wg: *WaitGroup,
pol: *Pool, pol: *Pool,
path: []const u8, path: []const u8,
first: bool,
comptime on_finish: anytype,
finish_args: anytype,
) !void { ) !void {
if (op.verbose) {
std.fmt.format(op.verbose_logger, "extracting inode {} \"{s}\" to {s}...\n", .{ self.inode.hdr.num, self.name, path }) catch {};
}
return switch (self.inode.hdr.type) { return switch (self.inode.hdr.type) {
.dir, .ext_dir => self.extractDir(op, errs, wg, pol, path), .dir, .ext_dir => self.extractDir(op, errs, wg, pol, path, first),
.file, .ext_file => self.extractReg(op, errs, wg, pol, path), .file, .ext_file => self.extractReg(op, errs, wg, pol, path, first),
.symlink, .ext_symlink => self.extractSymlink(op, errs, wg, pol, path), .symlink, .ext_symlink => self.extractSymlink(op, errs, wg, pol, path, first),
.block_dev, .block_dev,
.ext_block_dev, .ext_block_dev,
.char_dev, .char_dev,
.ext_char_dev, .ext_char_dev,
.fifo, .fifo,
.ext_fifo, .ext_fifo,
=> self.extractDev(op, path), => {
try self.extractDev(op, path);
if (!first) self.deinit();
return;
},
else => { else => {
if (op.verbose) { if (op.verbose) {
std.fmt.format( std.fmt.format(
op.verbose_logger.?, op.verbose_logger,
"inode {} \"{s}\" is a socket file. Ignoring.\n", "inode {} \"{s}\" is a socket file. Ignoring.\n",
.{ self.inode.hdr.num, self.name }, .{ self.inode.hdr.num, self.name },
) catch {}; ) catch {};
@@ -256,31 +264,104 @@ pub fn File(comptime T: type) type {
}, },
}; };
} }
fn extractDir(self: Self, op: *ExtractionOptions, errs: *std.ArrayList(anyerror), wg: *WaitGroup, pol: *Pool, path: []const u8) !void { fn extractDir(
self: Self,
op: ExtractionOptions,
errs: *std.ArrayList(anyerror),
wg: *WaitGroup,
pol: *Pool,
path: []const u8,
comptime on_finish: anytype,
finish_args: anytype,
) !void {
if (errs.items.len > 0) return; if (errs.items.len > 0) return;
_ = self; wg.start();
_ = op; var dir_wg: WaitGroup = .{};
_ = wg; dir_wg.startMany(self.entries.?.len);
_ = pol; for (self.entries.?) |e| {
_ = path; const fil: Self = try .initFromEntry(self.rdr, e);
}
return error{TODO}.TODO; return error{TODO}.TODO;
} }
fn extractReg(self: Self, op: *ExtractionOptions, errs: *std.ArrayList(anyerror), wg: *WaitGroup, pol: *Pool, path: []const u8) !void { fn extractReg(
self: Self,
op: ExtractionOptions,
errs: *std.ArrayList(anyerror),
wg: *WaitGroup,
pol: *Pool,
path: []const u8,
first: bool,
comptime on_finish: anytype,
finish_args: anytype,
) !void {
if (errs.items.len > 0) return; if (errs.items.len > 0) return;
const fil = try std.fs.cwd().createFile(path, .{}); const fil = try std.fs.cwd().createFile(path, .{});
@constCast(&self.data_reader.?).setPool(pol); @constCast(&self.data_reader.?).setPool(pol);
wg.start(); wg.start();
try self.data_reader.?.writeToNoBlock(errs, fil, wg, extractRegFinish, .{ self, fil }); var fil_errs: std.ArrayList(anyerror) = .init(self.rdr.alloc);
_ = op; try self.data_reader.?.writeToNoBlock(fil_errs, fil, wg, extractRegFinish, .{ self, op, fil, &fil_errs, first });
//TODO: add some way of verbose logging of the errors for this file in particular.
return; return;
} }
fn extractRegFinish(self: Self, fil: std.fs.File) void { fn extractRegFinish(
self: Self,
op: ExtractionOptions,
fil: std.fs.File,
errs: *std.ArrayList(anyerror),
fil_errs: *std.ArrayList(anyerror),
first: bool,
comptime on_finish: anytype,
finish_args: anytype,
) void {
defer fil.close(); defer fil.close();
//TODO: set owners & permissions. Check if we need to call self.deinit(); defer fil_errs.deinit();
_ = self; defer if (!first) self.deinit();
if (fil_errs.items.len > 0) {
if (op.verbose) {
for (fil_errs.items) |err| {
std.fmt.format(op.verbose_logger, "error extracting inode {} \"{s}\": {}\n", .{ self.inode.num, self.name, err }) catch {};
}
}
errs.append(fil_errs.items[0]) catch {};
return;
}
if (!op.ignore_permissions) {
const fil_uid = self.uid() catch |err| {
if (op.verbose) {
std.fmt.format(op.verbose_logger, "error getting uid: {}\n", .{err}) catch {};
return;
}
};
const fil_gid = self.gid() catch |err| {
if (op.verbose) {
std.fmt.format(op.verbose_logger, "error getting gid: {}\n", .{err}) catch {};
return;
}
};
fil.chmod(self.inode.hdr.perm) catch |err| {
if (op.verbose) {
std.fmt.format(op.verbose_logger, "error setting permissions: {}\n", .{err}) catch {};
return;
}
};
fil.chown(fil_uid, fil_gid) catch |err| {
if (op.verbose) {
std.fmt.format(op.verbose_logger, "error setting owners: {}\n", .{err}) catch {};
return;
}
};
}
} }
fn extractSymlink(self: Self, op: *ExtractionOptions, errs: *std.ArrayList(anyerror), wg: *WaitGroup, pol: *Pool, path: []const u8) !void { fn extractSymlink(
self: Self,
op: ExtractionOptions,
errs: *std.ArrayList(anyerror),
wg: *WaitGroup,
pol: *Pool,
path: []const u8,
first: bool,
comptime on_finish: anytype,
finish_args: anytype,
) !void {
if (errs.items.len > 0) return; if (errs.items.len > 0) return;
_ = self; _ = self;
_ = op; _ = op;
@@ -289,11 +370,17 @@ pub fn File(comptime T: type) type {
_ = path; _ = path;
return error{TODO}.TODO; return error{TODO}.TODO;
} }
fn extractDev(self: Self, op: *ExtractionOptions, path: []const u8) !void { fn extractDev(
self: Self,
op: ExtractionOptions,
path: []const u8,
comptime on_finish: anytype,
finish_args: anytype,
) !void {
if (comptime builtin.os.tag != .linux) { if (comptime builtin.os.tag != .linux) {
if (op.verbose) { if (op.verbose) {
std.fmt.format( std.fmt.format(
op.verbose_logger.?, op.verbose_logger,
"inode {} \"{s}\" is a device/fifo file and the OS is not Linux. Ignoring.\n", "inode {} \"{s}\" is a device/fifo file and the OS is not Linux. Ignoring.\n",
.{ self.inode.hdr.num, self.name }, .{ self.inode.hdr.num, self.name },
) catch {}; ) catch {};
+1 -1
View File
@@ -34,5 +34,5 @@ test "ExtractFile" {
defer fil.deinit(); defer fil.deinit();
var op: ExtractionOptions = try .init(); var op: ExtractionOptions = try .init();
op.verbose = true; op.verbose = true;
try fil.extract(&op, file_extr_loc); try fil.extract(op, file_extr_loc);
} }