Slight improvement to how permissions are applied to folders
unsquashfs Verbose flag
This commit is contained in:
@@ -16,6 +16,7 @@ const help_mgs =
|
|||||||
\\ -o <offset> Start reading the archive at the given offset.
|
\\ -o <offset> Start reading the archive at the given offset.
|
||||||
\\
|
\\
|
||||||
\\ -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
|
||||||
\\
|
\\
|
||||||
\\ --help Display this messages
|
\\ --help Display this messages
|
||||||
\\ --version Display the version
|
\\ --version Display the version
|
||||||
@@ -28,6 +29,7 @@ var archive: []const u8 = "";
|
|||||||
var extLoc: []const u8 = "squashfs-root";
|
var extLoc: []const u8 = "squashfs-root";
|
||||||
var offset: u64 = 0;
|
var offset: u64 = 0;
|
||||||
var threads: u32 = 0;
|
var threads: u32 = 0;
|
||||||
|
var verbose: bool = false;
|
||||||
|
|
||||||
pub fn main() !void {
|
pub fn main() !void {
|
||||||
const alloc = std.heap.smp_allocator;
|
const alloc = std.heap.smp_allocator;
|
||||||
@@ -44,7 +46,7 @@ pub fn main() !void {
|
|||||||
defer fil.close();
|
defer fil.close();
|
||||||
var arc: squashfs.Archive = try .initAdvanced(alloc, fil, offset, threads); //TODO: Update when memory size matters. //TODO: Handle error gracefully.
|
var arc: squashfs.Archive = try .initAdvanced(alloc, fil, offset, threads); //TODO: Update when memory size matters. //TODO: Handle error gracefully.
|
||||||
defer arc.deinit();
|
defer arc.deinit();
|
||||||
try arc.extract(extLoc, .Default); //TODO: Handle error gracefully.
|
try arc.extract(extLoc, if (verbose) .VerboseDefault(&out.interface) else .Default); //TODO: Handle error gracefully.
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handleArgs(alloc: std.mem.Allocator, out: *Writer) !void {
|
fn handleArgs(alloc: std.mem.Allocator, out: *Writer) !void {
|
||||||
@@ -82,6 +84,9 @@ fn handleArgs(alloc: std.mem.Allocator, out: *Writer) !void {
|
|||||||
return errors.InvalidArguments;
|
return errors.InvalidArguments;
|
||||||
};
|
};
|
||||||
continue;
|
continue;
|
||||||
|
} else if (std.mem.eql(u8, arg, "-v")) {
|
||||||
|
verbose = 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);
|
||||||
|
|||||||
+85
-33
@@ -4,6 +4,7 @@ const std = @import("std");
|
|||||||
const Reader = std.Io.Reader;
|
const Reader = std.Io.Reader;
|
||||||
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;
|
||||||
|
|
||||||
const Archive = @import("archive.zig");
|
const Archive = @import("archive.zig");
|
||||||
const DirEntry = @import("dir_entry.zig");
|
const DirEntry = @import("dir_entry.zig");
|
||||||
@@ -186,11 +187,58 @@ pub fn extractTo(self: Inode, archive: *Archive, path: []const u8, options: Extr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const Perms = struct {
|
const Parent = struct {
|
||||||
|
alloc: std.mem.Allocator,
|
||||||
|
|
||||||
path: []const u8,
|
path: []const u8,
|
||||||
uid: u16,
|
uid: u16,
|
||||||
gid: u16,
|
gid: u16,
|
||||||
perm: u16,
|
perm: u16,
|
||||||
|
mod_time: u32,
|
||||||
|
|
||||||
|
ignore_permissions: bool,
|
||||||
|
ignore_xattr: bool,
|
||||||
|
|
||||||
|
wg: WaitGroup,
|
||||||
|
mut: Mutex = .{},
|
||||||
|
|
||||||
|
fn create(alloc: std.mem.Allocator, hdr: Header, archive: *Archive, path: []const u8, options: ExtractionOptions, dir_size: usize) !*Parent {
|
||||||
|
const out = try alloc.create(Parent);
|
||||||
|
errdefer alloc.destroy(out);
|
||||||
|
out.* = .{
|
||||||
|
.alloc = alloc,
|
||||||
|
|
||||||
|
.path = path,
|
||||||
|
.uid = try archive.id(hdr.uid_idx),
|
||||||
|
.gid = try archive.id(hdr.gid_idx),
|
||||||
|
.perm = hdr.permissions,
|
||||||
|
.mod_time = hdr.mod_time,
|
||||||
|
|
||||||
|
.ignore_permissions = options.ignore_permissions,
|
||||||
|
.ignore_xattr = options.ignore_xattr,
|
||||||
|
|
||||||
|
.wg = .{ .state = .init(dir_size) },
|
||||||
|
};
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn finish(p: *Parent) !void {
|
||||||
|
p.mut.lock();
|
||||||
|
{
|
||||||
|
defer p.mut.unlock();
|
||||||
|
p.wg.finish();
|
||||||
|
if (!p.wg.isDone()) return;
|
||||||
|
}
|
||||||
|
defer p.alloc.destroy(p);
|
||||||
|
var fil = try std.fs.cwd().openFile(p.path, .{});
|
||||||
|
defer fil.close();
|
||||||
|
const time = p.mod_time * 1000000000;
|
||||||
|
try fil.updateTimes(time, time);
|
||||||
|
if (p.ignore_permissions) {
|
||||||
|
try fil.chmod(p.perm);
|
||||||
|
try fil.chown(p.uid, p.gid);
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Extract the inode to the given path. Multi-threaded.
|
/// Extract the inode to the given path. Multi-threaded.
|
||||||
@@ -217,7 +265,6 @@ pub fn extractToThreaded(self: Inode, archive: *Archive, path: []const u8, optio
|
|||||||
const alloc = thread_alloc.allocator();
|
const alloc = thread_alloc.allocator();
|
||||||
|
|
||||||
var wg: WaitGroup = .{};
|
var wg: WaitGroup = .{};
|
||||||
var perms: ?std.ArrayList(Perms) = if (options.ignore_permissions) null else try .initCapacity(alloc, 100);
|
|
||||||
// defer if(!options.ignore_permissions) perms.?.deinit(alloc); We don't need to do this due to ArenaAllocator
|
// defer if(!options.ignore_permissions) perms.?.deinit(alloc); We don't need to do this due to ArenaAllocator
|
||||||
var pool: Pool = undefined;
|
var pool: Pool = undefined;
|
||||||
try pool.init(.{ .allocator = alloc, .n_jobs = threads - 1 });
|
try pool.init(.{ .allocator = alloc, .n_jobs = threads - 1 });
|
||||||
@@ -225,19 +272,17 @@ pub fn extractToThreaded(self: Inode, archive: *Archive, path: []const u8, optio
|
|||||||
var out_err: ?anyerror = null;
|
var out_err: ?anyerror = null;
|
||||||
|
|
||||||
wg.start();
|
wg.start();
|
||||||
self.extractThread(alloc, archive, path, options, &wg, &pool, &out_err, &perms);
|
self.extractThread(alloc, archive, path, options, &wg, &pool, &out_err, null);
|
||||||
pool.waitAndWork(&wg);
|
pool.waitAndWork(&wg);
|
||||||
if (out_err != null) return out_err.?;
|
if (out_err != null) return out_err.?;
|
||||||
|
|
||||||
if (perms != null) {
|
var fil = try std.fs.cwd().openFile(path, .{});
|
||||||
for (perms.?.items) |p| {
|
|
||||||
var fil = try std.fs.cwd().openFile(p.path, .{});
|
|
||||||
defer fil.close();
|
defer fil.close();
|
||||||
const time = @as(i128, self.hdr.mod_time) * 1000000000;
|
const time = @as(i128, self.hdr.mod_time) * 1000000000;
|
||||||
try fil.updateTimes(time, time);
|
try fil.updateTimes(time, time);
|
||||||
try fil.chmod(p.perm);
|
if (options.ignore_permissions) {
|
||||||
try fil.chown(p.uid, p.gid);
|
try fil.chmod(self.hdr.permissions);
|
||||||
}
|
try fil.chown(try archive.id(self.hdr.uid_idx), try archive.id(self.hdr.gid_idx));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.file, .ext_file => {
|
.file, .ext_file => {
|
||||||
@@ -272,7 +317,7 @@ fn extractThreadEntry(
|
|||||||
wg: *WaitGroup,
|
wg: *WaitGroup,
|
||||||
pool: *Pool,
|
pool: *Pool,
|
||||||
out_err: *?anyerror,
|
out_err: *?anyerror,
|
||||||
perms: *?std.ArrayList(Perms),
|
parent: ?*Parent,
|
||||||
) void {
|
) void {
|
||||||
var new_path = alloc.alloc(u8, path.len + entry.name.len + 1) catch |err| {
|
var new_path = alloc.alloc(u8, path.len + entry.name.len + 1) catch |err| {
|
||||||
wg.finish();
|
wg.finish();
|
||||||
@@ -287,7 +332,7 @@ fn extractThreadEntry(
|
|||||||
wg.finish();
|
wg.finish();
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
inode.extractThread(alloc, archive, new_path, options, wg, pool, out_err, perms);
|
inode.extractThread(alloc, archive, new_path, options, wg, pool, out_err, parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Extract threadedly the inode to the path.
|
/// Extract threadedly the inode to the path.
|
||||||
@@ -300,18 +345,35 @@ fn extractThread(
|
|||||||
wg: *WaitGroup,
|
wg: *WaitGroup,
|
||||||
pool: *Pool,
|
pool: *Pool,
|
||||||
out_err: *?anyerror,
|
out_err: *?anyerror,
|
||||||
perms: *?std.ArrayList(Perms),
|
parent: ?*Parent,
|
||||||
) void {
|
) void {
|
||||||
defer wg.finish();
|
if (options.verbose)
|
||||||
|
options.verbose_writer.?.print("Extracting inode #{} to {s}\n", .{ self.hdr.num, path }) catch {};
|
||||||
|
defer {
|
||||||
|
if (parent != null) parent.?.finish() catch |err| {
|
||||||
|
if (options.verbose)
|
||||||
|
options.verbose_writer.?.print("Error setting folder permission to {s}: {}\n", .{ path, err }) catch {};
|
||||||
|
out_err.* = err;
|
||||||
|
};
|
||||||
|
wg.finish();
|
||||||
|
}
|
||||||
if (out_err.* != null) return;
|
if (out_err.* != null) return;
|
||||||
switch (self.hdr.inode_type) {
|
switch (self.hdr.inode_type) {
|
||||||
.dir, .ext_dir => {
|
.dir, .ext_dir => {
|
||||||
_ = std.fs.cwd().makePathStatus(path) catch |err| {
|
_ = std.fs.cwd().makePathStatus(path) catch |err| {
|
||||||
|
if (options.verbose)
|
||||||
|
options.verbose_writer.?.print("Error creating {s}: {}\n", .{ path, err }) catch {};
|
||||||
out_err.* = err;
|
out_err.* = err;
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
const entries = self.dirEntries(alloc, archive.*) catch |err| {
|
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 p = Parent.create(alloc, self.hdr, archive, path, options, entries.len) catch |err| {
|
||||||
out_err.* = err;
|
out_err.* = err;
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
@@ -319,7 +381,7 @@ fn extractThread(
|
|||||||
// defer files.deinit(alloc); We don't need to do this due to ArenaAllocator
|
// defer files.deinit(alloc); We don't need to do this due to ArenaAllocator
|
||||||
for (entries) |entry| {
|
for (entries) |entry| {
|
||||||
if (entry.inode_type == .dir) {
|
if (entry.inode_type == .dir) {
|
||||||
extractThreadEntry(entry, alloc, archive, path, options, wg, pool, out_err, perms);
|
extractThreadEntry(entry, alloc, archive, path, options, wg, pool, out_err, p);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
pool.spawn(
|
pool.spawn(
|
||||||
@@ -333,45 +395,35 @@ fn extractThread(
|
|||||||
wg,
|
wg,
|
||||||
pool,
|
pool,
|
||||||
out_err,
|
out_err,
|
||||||
perms,
|
p,
|
||||||
},
|
},
|
||||||
) catch |err| {
|
) catch |err| {
|
||||||
wg.finish();
|
wg.finish();
|
||||||
|
if (options.verbose)
|
||||||
|
options.verbose_writer.?.print("Error starting extraction thread: {}\n", .{err}) catch {};
|
||||||
out_err.* = err;
|
out_err.* = err;
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if (!options.ignore_permissions) {
|
|
||||||
const new_val = perms.*.?.addOne(alloc) catch |err| {
|
|
||||||
out_err.* = err;
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
new_val.* = .{
|
|
||||||
.path = path,
|
|
||||||
.uid = archive.id(self.hdr.uid_idx) catch |err| {
|
|
||||||
out_err.* = err;
|
|
||||||
return;
|
|
||||||
},
|
|
||||||
.gid = archive.id(self.hdr.gid_idx) catch |err| {
|
|
||||||
out_err.* = err;
|
|
||||||
return;
|
|
||||||
},
|
|
||||||
.perm = self.hdr.permissions,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
.file, .ext_file => {
|
.file, .ext_file => {
|
||||||
self.extractRegFileThreaded(alloc, archive, path, options, pool) catch |err| {
|
self.extractRegFileThreaded(alloc, archive, path, options, pool) catch |err| {
|
||||||
|
if (options.verbose)
|
||||||
|
options.verbose_writer.?.print("Error extracting file inode #{} to {s}: {}\n", .{ self.hdr.num, path, err }) catch {};
|
||||||
out_err.* = err;
|
out_err.* = err;
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
.symlink, .ext_symlink => {
|
.symlink, .ext_symlink => {
|
||||||
self.extractSymlink(path) catch |err| {
|
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;
|
out_err.* = err;
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
else => {
|
else => {
|
||||||
self.extractDevice(archive, path, options) catch |err| {
|
self.extractDevice(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;
|
out_err.* = err;
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user