Move extract logic to util/extract.zig to make it easier to read.
This commit is contained in:
+2
-348
@@ -14,6 +14,7 @@ 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 DataReader = @import("util/data.zig");
|
||||||
const ThreadedDataReader = @import("util/data_threaded.zig");
|
const ThreadedDataReader = @import("util/data_threaded.zig");
|
||||||
|
const InodeExtract = @import("util/extract.zig");
|
||||||
const InodeFinish = @import("util/inode_finish.zig");
|
const InodeFinish = @import("util/inode_finish.zig");
|
||||||
const FinishUnion = InodeFinish.FinishUnion;
|
const FinishUnion = InodeFinish.FinishUnion;
|
||||||
const MetadataReader = @import("util/metadata.zig");
|
const MetadataReader = @import("util/metadata.zig");
|
||||||
@@ -126,20 +127,6 @@ fn readerFromData(alloc: std.mem.Allocator, archive: *Archive, data: anytype) !D
|
|||||||
out.addFragment(try archive.frag_table.get(data.frag_idx), data.frag_block_offset);
|
out.addFragment(try archive.frag_table.get(data.frag_idx), data.frag_block_offset);
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
/// Get a threaded data reader for a file inode.
|
|
||||||
pub fn threadedDataReader(self: Inode, alloc: std.mem.Allocator, archive: *Archive) !ThreadedDataReader {
|
|
||||||
return switch (self.hdr.inode_type) {
|
|
||||||
.file => threadedReaderFromData(alloc, archive, self.data.file),
|
|
||||||
.ext_file => threadedReaderFromData(alloc, archive, self.data.ext_file),
|
|
||||||
else => error.NotRegularFile,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
fn threadedReaderFromData(alloc: std.mem.Allocator, archive: *Archive, data: anytype) !ThreadedDataReader {
|
|
||||||
var out: ThreadedDataReader = .init(alloc, archive.*, data.block_sizes, data.block_start, data.size);
|
|
||||||
if (data.frag_idx != 0xFFFFFFFF)
|
|
||||||
out.addFragment(try archive.frag_table.get(data.frag_idx), data.frag_block_offset);
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the directory entries for a directory inode.
|
/// Get the directory entries for a directory inode.
|
||||||
pub fn dirEntries(self: Inode, alloc: std.mem.Allocator, archive: Archive) ![]DirEntry {
|
pub fn dirEntries(self: Inode, alloc: std.mem.Allocator, archive: Archive) ![]DirEntry {
|
||||||
@@ -198,338 +185,5 @@ pub fn setMetadata(self: Inode, alloc: std.mem.Allocator, archive: *Archive, fil
|
|||||||
|
|
||||||
/// Extract the inode to the given path.
|
/// Extract the inode to the given path.
|
||||||
pub fn extractTo(self: Inode, alloc: std.mem.Allocator, archive: *Archive, path: []const u8, options: ExtractionOptions) !void {
|
pub fn extractTo(self: Inode, alloc: std.mem.Allocator, archive: *Archive, path: []const u8, options: ExtractionOptions) !void {
|
||||||
if (options.threads > 1) return self.extractToThreaded(alloc, archive, path, options);
|
return InodeExtract.extractTo(alloc, self, archive, path, options);
|
||||||
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(alloc, archive, path[0 .. path.len - 1], options);
|
|
||||||
std.fs.cwd().makeDir(path) catch |err| {
|
|
||||||
if (err != std.fs.Dir.MakeError.PathAlreadyExists) return err;
|
|
||||||
};
|
|
||||||
const entries = try self.dirEntries(alloc, 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 inode: Inode = try readFromEntry(alloc, archive, entry);
|
|
||||||
defer inode.deinit(alloc);
|
|
||||||
try inode.extractTo(alloc, archive, new_path, options);
|
|
||||||
}
|
|
||||||
|
|
||||||
var fil = try std.fs.cwd().openFile(path, .{});
|
|
||||||
defer fil.close();
|
|
||||||
try self.setMetadata(alloc, archive, fil, options);
|
|
||||||
},
|
|
||||||
.file, .ext_file => try self.extractRegFile(alloc, archive, path, options),
|
|
||||||
.symlink, .ext_symlink => try self.extractSymlink(path),
|
|
||||||
else => try self.extractDevice(alloc, archive, path, options),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Extract the inode to the given path. Multi-threaded.
|
|
||||||
/// Functions identically to extractTo on all but regular files and directories.
|
|
||||||
fn extractToThreaded(self: Inode, allocator: std.mem.Allocator, 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.extractToThreaded(allocator, archive, path[0 .. path.len - 1], options);
|
|
||||||
|
|
||||||
// Arena Allocator
|
|
||||||
var stack_alloc = std.heap.stackFallback(1024 * 1024, allocator);
|
|
||||||
var arena_alloc: std.heap.ArenaAllocator = .init(stack_alloc.get());
|
|
||||||
defer arena_alloc.deinit();
|
|
||||||
var thread_alloc: std.heap.ThreadSafeAllocator = .{ .child_allocator = arena_alloc.allocator() };
|
|
||||||
const alloc = thread_alloc.allocator();
|
|
||||||
|
|
||||||
var wg: WaitGroup = .{};
|
|
||||||
// defer if(!options.ignore_permissions) perms.?.deinit(alloc); We don't need to do this due to ArenaAllocator
|
|
||||||
var pool: Pool = undefined;
|
|
||||||
try pool.init(.{ .allocator = alloc, .n_jobs = options.threads - 1 });
|
|
||||||
defer pool.deinit();
|
|
||||||
var out_err: ?anyerror = null;
|
|
||||||
|
|
||||||
wg.start();
|
|
||||||
self.extractThread(alloc, archive, path, options, .{ .wg = &wg }, &pool, &out_err);
|
|
||||||
pool.waitAndWork(&wg);
|
|
||||||
if (out_err != null) return out_err.?;
|
|
||||||
|
|
||||||
var fil = try std.fs.cwd().openFile(path, .{});
|
|
||||||
defer fil.close();
|
|
||||||
try self.setMetadata(alloc, archive, fil, options);
|
|
||||||
},
|
|
||||||
.file, .ext_file => {
|
|
||||||
var pool: Pool = undefined;
|
|
||||||
try pool.init(.{ .allocator = allocator, .n_jobs = options.threads - 1 });
|
|
||||||
defer pool.deinit();
|
|
||||||
|
|
||||||
// Arena Allocator
|
|
||||||
var stack_alloc = std.heap.stackFallback(1024 * 1024, allocator);
|
|
||||||
var arena_alloc: std.heap.ArenaAllocator = .init(stack_alloc.get());
|
|
||||||
defer arena_alloc.deinit();
|
|
||||||
var thread_alloc: std.heap.ThreadSafeAllocator = .{ .child_allocator = arena_alloc.allocator() };
|
|
||||||
const alloc = thread_alloc.allocator();
|
|
||||||
|
|
||||||
var wg: WaitGroup = .{};
|
|
||||||
var out_err: ?anyerror = null;
|
|
||||||
|
|
||||||
self.extractThread(alloc, archive, path, options, .{ .wg = &wg }, &pool, &out_err);
|
|
||||||
pool.waitAndWork(&wg);
|
|
||||||
|
|
||||||
if (out_err != null) return out_err.?;
|
|
||||||
|
|
||||||
var fil = try std.fs.cwd().openFile(path, .{});
|
|
||||||
defer fil.close();
|
|
||||||
try self.setMetadata(alloc, archive, fil, options);
|
|
||||||
},
|
|
||||||
.symlink, .ext_symlink => try self.extractSymlink(path),
|
|
||||||
else => try self.extractDevice(allocator, archive, path, options),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn extractThreadEntry(
|
|
||||||
entry: DirEntry,
|
|
||||||
alloc: std.mem.Allocator,
|
|
||||||
archive: *Archive,
|
|
||||||
path: []const u8,
|
|
||||||
options: ExtractionOptions,
|
|
||||||
finish: FinishUnion,
|
|
||||||
pool: *Pool,
|
|
||||||
out_err: *?anyerror,
|
|
||||||
) void {
|
|
||||||
var new_path = alloc.alloc(u8, path.len + entry.name.len + 1) catch |err| {
|
|
||||||
finish.finish();
|
|
||||||
out_err.* = err;
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
@memcpy(new_path[0..path.len], path);
|
|
||||||
@memcpy(new_path[path.len + 1 ..], entry.name);
|
|
||||||
new_path[path.len] = '/';
|
|
||||||
var inode = readFromEntry(alloc, archive, entry) catch |err| {
|
|
||||||
out_err.* = err;
|
|
||||||
finish.finish();
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
inode.extractThread(alloc, archive, new_path, options, finish, pool, out_err);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Extract threadedly the inode to the path.
|
|
||||||
fn extractThread(
|
|
||||||
self: Inode,
|
|
||||||
alloc: std.mem.Allocator,
|
|
||||||
archive: *Archive,
|
|
||||||
path: []const u8,
|
|
||||||
options: ExtractionOptions,
|
|
||||||
finish: FinishUnion,
|
|
||||||
pool: *Pool,
|
|
||||||
out_err: *?anyerror,
|
|
||||||
) void {
|
|
||||||
if (options.verbose)
|
|
||||||
options.verbose_writer.?.print("Extracting inode #{} to {s}\n", .{ self.hdr.num, path }) catch {};
|
|
||||||
defer finish.finish();
|
|
||||||
if (out_err.* != null) return;
|
|
||||||
switch (self.hdr.inode_type) {
|
|
||||||
.dir, .ext_dir => {
|
|
||||||
_ = std.fs.cwd().makePathStatus(path) catch |err| {
|
|
||||||
if (options.verbose)
|
|
||||||
options.verbose_writer.?.print("Error creating {s}: {}\n", .{ path, err }) catch {};
|
|
||||||
out_err.* = err;
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
const entries = self.dirEntries(alloc, archive.*) catch |err| {
|
|
||||||
if (options.verbose)
|
|
||||||
options.verbose_writer.?.print("Error getting directory entries for inode #{} (extracting to {s}): {}\n", .{ self.hdr.num, path, err }) catch {};
|
|
||||||
out_err.* = err;
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
const fin = InodeFinish.create(
|
|
||||||
alloc,
|
|
||||||
self,
|
|
||||||
path,
|
|
||||||
archive,
|
|
||||||
options,
|
|
||||||
finish,
|
|
||||||
out_err,
|
|
||||||
null,
|
|
||||||
entries.len,
|
|
||||||
) catch |err| {
|
|
||||||
if (options.verbose)
|
|
||||||
options.verbose_writer.?.print("Error allocating memory\n", .{}) catch {};
|
|
||||||
out_err.* = err;
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
// defer files.deinit(alloc); We don't need to do this due to ArenaAllocator
|
|
||||||
for (entries) |entry| {
|
|
||||||
if (entry.inode_type == .dir) {
|
|
||||||
extractThreadEntry(
|
|
||||||
entry,
|
|
||||||
alloc,
|
|
||||||
archive,
|
|
||||||
path,
|
|
||||||
options,
|
|
||||||
.{ .fin = fin },
|
|
||||||
pool,
|
|
||||||
out_err,
|
|
||||||
);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
pool.spawn(
|
|
||||||
extractThreadEntry,
|
|
||||||
.{
|
|
||||||
entry,
|
|
||||||
alloc,
|
|
||||||
archive,
|
|
||||||
path,
|
|
||||||
options,
|
|
||||||
FinishUnion{ .fin = fin },
|
|
||||||
pool,
|
|
||||||
out_err,
|
|
||||||
},
|
|
||||||
) catch |err| {
|
|
||||||
fin.finish();
|
|
||||||
if (options.verbose)
|
|
||||||
options.verbose_writer.?.print("Error starting extraction thread: {}\n", .{err}) catch {};
|
|
||||||
out_err.* = err;
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
},
|
|
||||||
.file, .ext_file => {
|
|
||||||
const fil = std.fs.cwd().createFile(path, .{}) catch |err| {
|
|
||||||
if (options.verbose)
|
|
||||||
options.verbose_writer.?.print("Error creating {s}: {}\n", .{ path, err }) catch {};
|
|
||||||
out_err.* = err;
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
var data = self.threadedDataReader(alloc, archive) catch |err| {
|
|
||||||
if (options.verbose)
|
|
||||||
options.verbose_writer.?.print(
|
|
||||||
"Error creating data reader for inode #{} (extracting to {s}): {}\n",
|
|
||||||
.{ self.hdr.num, path, err },
|
|
||||||
) catch {};
|
|
||||||
out_err.* = err;
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
const fin = InodeFinish.create(
|
|
||||||
alloc,
|
|
||||||
self,
|
|
||||||
path,
|
|
||||||
archive,
|
|
||||||
options,
|
|
||||||
finish,
|
|
||||||
out_err,
|
|
||||||
fil,
|
|
||||||
data.num_blocks,
|
|
||||||
) catch |err| {
|
|
||||||
if (options.verbose)
|
|
||||||
options.verbose_writer.?.print("Error allocating memory\n", .{}) catch {};
|
|
||||||
out_err.* = err;
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
data.extractThreaded(fil, pool, fin) catch |err| {
|
|
||||||
if (options.verbose)
|
|
||||||
options.verbose_writer.?.print("Error spawning threads: {}\n", .{err}) catch {};
|
|
||||||
out_err.* = err;
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
},
|
|
||||||
.symlink, .ext_symlink => {
|
|
||||||
self.extractSymlink(path) catch |err| {
|
|
||||||
if (options.verbose)
|
|
||||||
options.verbose_writer.?.print("Error extracting symlink inode #{} to {s}: {}\n", .{ self.hdr.num, path, err }) catch {};
|
|
||||||
out_err.* = err;
|
|
||||||
};
|
|
||||||
},
|
|
||||||
else => {
|
|
||||||
self.extractDevice(alloc, archive, path, options) catch |err| {
|
|
||||||
if (options.verbose)
|
|
||||||
options.verbose_writer.?.print("Error extracting device/IPC inode #{} to {s}: {}\n", .{ self.hdr.num, path, err }) catch {};
|
|
||||||
out_err.* = err;
|
|
||||||
};
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// 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, alloc: std.mem.Allocator, archive: *Archive, path: []const u8, options: ExtractionOptions) !void {
|
|
||||||
var fil = try std.fs.cwd().createFile(path, .{ .exclusive = true });
|
|
||||||
defer fil.close();
|
|
||||||
var wrt = fil.writer(&[0]u8{});
|
|
||||||
var dat_rdr = try self.dataReader(alloc, archive);
|
|
||||||
defer dat_rdr.deinit();
|
|
||||||
_ = try dat_rdr.interface.streamRemaining(&wrt.interface);
|
|
||||||
try wrt.interface.flush();
|
|
||||||
|
|
||||||
try self.setMetadata(alloc, archive, fil, options);
|
|
||||||
}
|
|
||||||
/// 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, alloc: std.mem.Allocator, 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, .{});
|
|
||||||
defer fil.close();
|
|
||||||
try self.setMetadata(alloc, archive, fil, options);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,307 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
const Pool = std.Thread.Pool;
|
||||||
|
const WaitGroup = std.Thread.WaitGroup;
|
||||||
|
|
||||||
|
const Archive = @import("../archive.zig");
|
||||||
|
const DirEntry = @import("../dir_entry.zig");
|
||||||
|
const Inode = @import("../inode.zig");
|
||||||
|
const ExtractionOptions = @import("../options.zig");
|
||||||
|
const InodeFinish = @import("inode_finish.zig");
|
||||||
|
const FinishUnion = InodeFinish.FinishUnion;
|
||||||
|
const ThreadedDataReader = @import("data_threaded.zig");
|
||||||
|
|
||||||
|
// 1 MB
|
||||||
|
const STACK_ALLOC_SIZE = 1024 * 1024;
|
||||||
|
|
||||||
|
pub fn extractTo(
|
||||||
|
allocator: Allocator,
|
||||||
|
inode: Inode,
|
||||||
|
archive: *Archive,
|
||||||
|
path: []const u8,
|
||||||
|
options: ExtractionOptions,
|
||||||
|
) !void {
|
||||||
|
if (path[path.len - 1] == '/')
|
||||||
|
return extractTo(allocator, inode, archive, path[0 .. path.len - 2], options);
|
||||||
|
|
||||||
|
var stack_alloc = std.heap.stackFallback(STACK_ALLOC_SIZE, allocator);
|
||||||
|
var arena: std.heap.ArenaAllocator = .init(stack_alloc.get());
|
||||||
|
defer arena.deinit();
|
||||||
|
if (options.threads <= 1)
|
||||||
|
return extractToSingle(arena.allocator(), inode, archive, path, options);
|
||||||
|
|
||||||
|
var thread_alloc = std.heap.ThreadSafeAllocator{ .child_allocator = arena.allocator() };
|
||||||
|
const alloc = thread_alloc.allocator();
|
||||||
|
|
||||||
|
var pool_alloc = std.heap.stackFallback(10 * 1024, alloc);
|
||||||
|
var pool: Pool = undefined;
|
||||||
|
try pool.init(.{ .allocator = pool_alloc.get(), .n_jobs = options.threads - 1 });
|
||||||
|
|
||||||
|
var wg: WaitGroup = .{};
|
||||||
|
var err: ?anyerror = null;
|
||||||
|
extractToMulti(
|
||||||
|
alloc,
|
||||||
|
inode,
|
||||||
|
archive,
|
||||||
|
path,
|
||||||
|
options,
|
||||||
|
&pool,
|
||||||
|
.{ .wg = &wg },
|
||||||
|
&err,
|
||||||
|
);
|
||||||
|
pool.waitAndWork(&wg);
|
||||||
|
if (err != null) return err.?;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extractToSingle(
|
||||||
|
alloc: Allocator,
|
||||||
|
inode: Inode,
|
||||||
|
archive: *Archive,
|
||||||
|
path: []const u8,
|
||||||
|
options: ExtractionOptions,
|
||||||
|
) !void {
|
||||||
|
switch (inode.hdr.inode_type) {
|
||||||
|
.dir, .ext_dir => {
|
||||||
|
_ = std.fs.cwd().makeDir(path) catch |err| switch (err) {
|
||||||
|
std.fs.Dir.MakeError.PathAlreadyExists => {},
|
||||||
|
else => return err,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Currently we are ignoring any deinit or free calls since we know we are under an ArenaAllocator.
|
||||||
|
// Possibly in the future, do some simple math to see if it would be safe to ONLY deinit via Arena,
|
||||||
|
// otherwise be more conscientious about freeing memory.
|
||||||
|
// For now, this is good enough.
|
||||||
|
|
||||||
|
const entries = try inode.dirEntries(alloc, archive);
|
||||||
|
for (entries) |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 });
|
||||||
|
extractToSingle(alloc, sub_inode, archive, new_path, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
const fil = try std.fs.cwd().openFile(path);
|
||||||
|
defer fil.close();
|
||||||
|
try inode.setMetadata(alloc, archive, fil, options);
|
||||||
|
},
|
||||||
|
.file, .ext_file => {
|
||||||
|
var fil = try std.fs.cwd().createFile(path, .{ .exclusive = true });
|
||||||
|
defer fil.close();
|
||||||
|
var wrt = fil.writer(&[0]u8{});
|
||||||
|
var dat_rdr = try inode.dataReader(alloc, archive);
|
||||||
|
defer dat_rdr.deinit();
|
||||||
|
_ = try dat_rdr.interface.streamRemaining(&wrt.interface);
|
||||||
|
try wrt.interface.flush();
|
||||||
|
|
||||||
|
try inode.setMetadata(alloc, archive, fil, options);
|
||||||
|
},
|
||||||
|
.symlink, .ext_symlink => return extractSymlink(inode, path),
|
||||||
|
else => return extractDeviceAndIPC(inode, alloc, archive, path, options),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extractToMulti(
|
||||||
|
alloc: Allocator,
|
||||||
|
inode: Inode,
|
||||||
|
archive: *Archive,
|
||||||
|
path: []const u8,
|
||||||
|
options: ExtractionOptions,
|
||||||
|
pool: *Pool,
|
||||||
|
fin: FinishUnion,
|
||||||
|
err: *?anyerror,
|
||||||
|
) void {
|
||||||
|
if (err != null) {
|
||||||
|
fin.finish();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
switch (inode.hdr.inode_type) {
|
||||||
|
.dir, .ext_dir => {
|
||||||
|
_ = std.fs.cwd().makeDir(path) catch |res_err| switch (res_err) {
|
||||||
|
std.fs.Dir.MakeError.PathAlreadyExists => {},
|
||||||
|
else => {
|
||||||
|
err.* = res_err;
|
||||||
|
fin.finish();
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Currently we are ignoring any deinit or free calls since we know we are under an ArenaAllocator.
|
||||||
|
// Possibly in the future, do some simple math to see if it would be safe to ONLY deinit via Arena,
|
||||||
|
// otherwise be more conscientious about freeing memory.
|
||||||
|
// For now, this is good enough.
|
||||||
|
|
||||||
|
const entries = try inode.dirEntries(alloc, archive) catch |res_err| {
|
||||||
|
err.* = res_err;
|
||||||
|
fin.finish();
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
var dir_fin = InodeFinish.create(
|
||||||
|
alloc,
|
||||||
|
inode,
|
||||||
|
path,
|
||||||
|
archive,
|
||||||
|
options,
|
||||||
|
fin,
|
||||||
|
err,
|
||||||
|
null,
|
||||||
|
entries.len,
|
||||||
|
) catch |res_err| {
|
||||||
|
err.* = res_err;
|
||||||
|
fin.finish();
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
for (entries) |ent| {
|
||||||
|
if (ent.inode_type == .dir)
|
||||||
|
extractEntry(alloc, ent, archive, path, options, pool, dir_fin, err);
|
||||||
|
|
||||||
|
pool.spawn(extractEntry, .{ alloc, ent, archive, path, options, pool, dir_fin, err }) catch |res_err| {
|
||||||
|
err.* = res_err;
|
||||||
|
dir_fin.finish();
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.file, .ext_file => {
|
||||||
|
const fil = std.fs.cwd().createFile(path, .{ .exclusive = true }) catch |res_err| {
|
||||||
|
err.* = res_err;
|
||||||
|
fin.finish();
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
var data_rdr = threadedDataReader(inode, alloc, archive) catch |res_err| {
|
||||||
|
err.* = res_err;
|
||||||
|
fin.finish();
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
const file_fin = InodeFinish.create(
|
||||||
|
alloc,
|
||||||
|
inode,
|
||||||
|
path,
|
||||||
|
archive,
|
||||||
|
options,
|
||||||
|
fin,
|
||||||
|
err,
|
||||||
|
fil,
|
||||||
|
data_rdr.num_blocks,
|
||||||
|
) catch |res_err| {
|
||||||
|
err.* = res_err;
|
||||||
|
fin.finish();
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
data_rdr.extractThreaded(fil, pool, file_fin);
|
||||||
|
},
|
||||||
|
.symlink, .ext_symlink => {
|
||||||
|
extractSymlink(inode, path) catch |res_err| {
|
||||||
|
err.* = res_err;
|
||||||
|
};
|
||||||
|
fin.finish();
|
||||||
|
},
|
||||||
|
else => {
|
||||||
|
extractDeviceAndIPC(inode, alloc, archive, path, options) catch |res_err| {
|
||||||
|
err.* = res_err;
|
||||||
|
};
|
||||||
|
fin.finish();
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fn extractEntry(
|
||||||
|
alloc: Allocator,
|
||||||
|
ent: DirEntry,
|
||||||
|
archive: *Archive,
|
||||||
|
path: []const u8,
|
||||||
|
options: ExtractionOptions,
|
||||||
|
pool: *Pool,
|
||||||
|
fin: FinishUnion,
|
||||||
|
err: *?anyerror,
|
||||||
|
) void {
|
||||||
|
const new_path = std.mem.concat(alloc, u8, []const []const u8{ path, "/", ent.name }) catch |res_err| {
|
||||||
|
err.* = res_err;
|
||||||
|
fin.finish();
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
const inode: Inode = .readFromEntry(alloc, archive, ent) catch |res_err| {
|
||||||
|
err.* = res_err;
|
||||||
|
fin.finish();
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
extractToMulti(alloc, inode, archive, new_path, options, pool, fin, err);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a threaded data reader for a file inode.
|
||||||
|
fn threadedDataReader(self: Inode, alloc: std.mem.Allocator, archive: *Archive) !ThreadedDataReader {
|
||||||
|
return switch (self.hdr.inode_type) {
|
||||||
|
.file => threadedReaderFromData(alloc, archive, self.data.file),
|
||||||
|
.ext_file => threadedReaderFromData(alloc, archive, self.data.ext_file),
|
||||||
|
else => error.NotRegularFile,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
fn threadedReaderFromData(alloc: std.mem.Allocator, archive: *Archive, data: anytype) !ThreadedDataReader {
|
||||||
|
var out: ThreadedDataReader = .init(alloc, archive.*, data.block_sizes, data.block_start, data.size);
|
||||||
|
if (data.frag_idx != 0xFFFFFFFF)
|
||||||
|
out.addFragment(try archive.frag_table.get(data.frag_idx), data.frag_block_offset);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates the symlink described by the inode.
|
||||||
|
/// Sets metadata.
|
||||||
|
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.
|
||||||
|
/// Sets metadata.
|
||||||
|
fn extractDeviceAndIPC(self: Inode, alloc: std.mem.Allocator, 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, .{});
|
||||||
|
defer fil.close();
|
||||||
|
try self.setMetadata(alloc, archive, fil, options);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user