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
-298
@@ -56,28 +56,7 @@ pub fn deinit(self: SfsFile) void {
|
||||
}
|
||||
|
||||
fn getEntries(self: SfsFile) ![]DirEntry {
|
||||
if (!self.isDir()) return FileError.NotDirectory;
|
||||
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);
|
||||
return self.inode.dirEntries(self.archive);
|
||||
}
|
||||
|
||||
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
|
||||
/// or, more specifically, it's inode's deinit function is called.
|
||||
pub fn dataReader(self: SfsFile) !DataReader {
|
||||
if (!self.isRegular()) return FileError.NotRegularFile;
|
||||
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;
|
||||
return self.inode.dataReader(self.archive);
|
||||
}
|
||||
|
||||
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);
|
||||
var pool: std.Thread.Pool = undefined;
|
||||
try pool.init(.{ .allocator = alloc, .n_jobs = 16 });
|
||||
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());
|
||||
//TODO: switch to threaded version.
|
||||
return self.inode.extractTo(self.archive, path, options);
|
||||
}
|
||||
|
||||
/// Utility function.
|
||||
|
||||
Reference in New Issue
Block a user