diff --git a/build.zig b/build.zig index 133f0db..78509ba 100644 --- a/build.zig +++ b/build.zig @@ -56,6 +56,9 @@ pub fn build(b: *std.Build) !void { b.installArtifact(lib); + const exe_config = b.addOptions(); + exe_config.addOption(std.SemanticVersion,"version", version); + const exe = b.addExecutable(.{ .name = "unsquashfs", .use_llvm = debug, @@ -65,9 +68,12 @@ pub fn build(b: *std.Build) !void { .target = target, .root_source_file = b.path("src/bin/unsquashfs.zig"), .valgrind = debug, + .imports = &.{ + .{ .name = "config", .module = exe_config.createModule() }, + .{ .name = "squashfs", .module = lib.root_module } + }, }), }); - exe.root_module.linkLibrary(lib); b.installArtifact(exe); diff --git a/build.zig.zon b/build.zig.zon index 4242f9c..d861362 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -2,7 +2,7 @@ .name = .squashfs, .version = "0.0.6", .fingerprint = 0x37ba29474b87f145, // Changing this has security and trust implications. - .minimum_zig_version = "0.15.2", + .minimum_zig_version = "0.16.0", .dependencies = .{ .zlib_ng = .{ .url = "git+https://github.com/CalebQ42/zig-zlib-ng#5f2f02dfb28acca2517dacbbd09e9b987f57b133", diff --git a/src/archive.zig b/src/archive.zig index faf161e..a052c78 100644 --- a/src/archive.zig +++ b/src/archive.zig @@ -5,6 +5,7 @@ const MemoryMap = File.MemoryMap; const Decomp = @import("decomp.zig"); const DecompCache = @import("decomp_cache.zig"); +const Extract = @import("extract.zig"); const ExtractionOptions = @import("options.zig"); const Inode = @import("inode.zig"); const SfsFile = @import("file.zig"); @@ -36,7 +37,7 @@ pub fn initAdvanced(alloc: std.mem.Allocator, io: Io, fil: File, offset: u64, ca return .{ .super = super, - .cache = .init( + .cache = try .init( alloc, map, super.compression, @@ -74,12 +75,15 @@ pub fn open(self: *Archive, alloc: std.mem.Allocator, io: Io, filepath: []const } pub fn extract(self: *Archive, alloc: std.mem.Allocator, io: Io, ext_loc: []const u8, options: ExtractionOptions) !void { - _ = self; - _ = alloc; - _ = io; - _ = ext_loc; - _ = options; - return error.TODO; + const root_inode: Inode = try .initRef( + alloc, + io, + &self.cache, + self.super.inode_start, + self.super.block_size, + self.super.root_ref, + ); + return Extract.extract(alloc, io, root_inode, &self.cache, self.super, ext_loc, options); } // Superblock @@ -93,7 +97,19 @@ pub const Superblock = extern struct { compression: Decomp.Enum, block_log: u16, flags: packed struct(u16) { - TODO: u16, + inode_uncompressed: bool, + data_uncompressed: bool, + check: bool, + frag_uncompressed: bool, + frag_never: bool, + frag_always: bool, + de_dupe: bool, + exportable: bool, + xattr_uncompressed: bool, + xattr_never: bool, + compression_options: bool, + id_uncompressed: bool, + _: u4, }, id_count: u16, ver_maj: u16, @@ -114,6 +130,8 @@ pub const Superblock = extern struct { return error.InvalidVersion; if (self.block_log != std.math.log2(self.block_size)) return error.BadBlockLog; + if (self.flags.check) + return error.BadCheckFlag; } }; @@ -127,10 +145,10 @@ test "Basics" { var archive_file = try Io.Dir.cwd().openFile(io, TestArchive, .{}); defer archive_file.close(io); - var arc: Archive = try .init(io, archive_file); + var arc: Archive = try .init(alloc, io, archive_file); defer arc.deinit(io); - var root_file = try arc.root(alloc); + var root_file = try arc.root(alloc, io); defer root_file.deinit(); } @@ -143,10 +161,10 @@ test "SingleFileExtraction" { var archive_file = try Io.Dir.cwd().openFile(io, TestArchive, .{}); defer archive_file.close(io); - var arc: Archive = try .init(io, archive_file); + var arc: Archive = try .init(alloc, io, archive_file); defer arc.deinit(io); - var ext_file = try arc.open(alloc, TestFile); + var ext_file = try arc.open(alloc, io, TestFile); defer ext_file.deinit(); try ext_file.extract(alloc, io, TestFileExtractLocation, .default); @@ -160,7 +178,7 @@ test "FullExtraction" { var archive_file = try Io.Dir.cwd().openFile(io, TestArchive, .{}); defer archive_file.close(io); - var arc: Archive = try .init(io, archive_file); + var arc: Archive = try .init(alloc, io, archive_file); defer arc.deinit(io); try arc.extract(alloc, io, TestFullExtractLocation, .default); diff --git a/src/bin/unsquashfs.zig b/src/bin/unsquashfs.zig index a945c01..d235679 100644 --- a/src/bin/unsquashfs.zig +++ b/src/bin/unsquashfs.zig @@ -1,9 +1,11 @@ const std = @import("std"); -const Writer = std.Io.Writer; +const Io = std.Io; +const Writer = Io.Writer; +const File = Io.File; const builtin = @import("builtin"); const config = @import("config"); -const squashfs = @import("zig_squashfs"); +const squashfs = @import("squashfs"); //TODO: Add more options const help_mgs = @@ -38,40 +40,41 @@ var ignore_xattrs: bool = false; var ignore_permissions: bool = false; var force: bool = false; -pub fn main() !void { - const alloc = std.heap.smp_allocator; - var stdout = std.fs.File.stdout(); - var out = stdout.writer(&[0]u8{}); +pub fn main(init: std.process.Init) !void { + const alloc = init.gpa; + const io = init.io; + + var stdout = File.stdout(); + var out = stdout.writer(io, &[0]u8{}); defer out.interface.flush() catch {}; - try handleArgs(alloc, &out.interface); + try handleArgs(init.minimal.args, &out.interface); if (archive.len == 0) { try out.interface.print("You must provide a squashfs archive\n", .{}); try out.interface.print(help_mgs, .{}); return; } - var fil: std.fs.File = try std.fs.cwd().openFile(archive, .{}); //TODO: Handle error gracefully. - defer fil.close(); - var arc: squashfs.Archive = try .init(alloc, fil, offset); //TODO: Update when memory size matters. //TODO: Handle error gracefully. - defer arc.deinit(); + var fil: File = try Io.Dir.cwd().openFile(io, archive, .{}); //TODO: Handle error gracefully. + defer fil.close(io); + var arc: squashfs.Archive = try .initAdvanced(alloc, io, fil, offset, 0); //TODO: Update when memory size matters. //TODO: Handle error gracefully. + defer arc.deinit(io); const options: squashfs.ExtractionOptions = .{ - .threads = if (threads == 0) try std.Thread.getCpuCount() else threads, .verbose = verbose, .verbose_writer = if (verbose) &out.interface else null, .ignore_xattr = ignore_xattrs, .ignore_permissions = ignore_permissions, }; if (force) - try std.fs.cwd().deleteTree(extLoc); - try arc.extract(alloc, extLoc, options); //TODO: Handle error gracefully. + try Io.Dir.cwd().deleteTree(io, extLoc); + try arc.extract(alloc, io, extLoc, options); //TODO: Handle error gracefully. } -fn handleArgs(alloc: std.mem.Allocator, out: *Writer) !void { - var args = try std.process.argsWithAllocator(alloc); - defer args.deinit(); - _ = args.next(); // args[0] is the application launch command. - while (args.next()) |arg| { +fn handleArgs(args: std.process.Args, out: *Writer) !void { + var arg_iter = args.iterate(); + defer arg_iter.deinit(); + _ = arg_iter.skip(); // args[0] is the application launch command. + while (arg_iter.next()) |arg| { if (std.mem.eql(u8, arg, "-o")) { - const nxt = args.next(); + const nxt = arg_iter.next(); if (nxt == null or nxt.?.len == 0) { try out.print("-o must be followed by a number\n", .{}); return errors.InvalidArguments; @@ -82,7 +85,7 @@ fn handleArgs(alloc: std.mem.Allocator, out: *Writer) !void { }; continue; } else if (std.mem.eql(u8, arg, "-d")) { - const nxt = args.next(); + const nxt = arg_iter.next(); if (nxt == null or nxt.?.len == 0) { try out.print("-d must be followed by a location\n", .{}); return errors.InvalidArguments; @@ -90,7 +93,7 @@ fn handleArgs(alloc: std.mem.Allocator, out: *Writer) !void { extLoc = nxt.?; continue; } else if (std.mem.eql(u8, arg, "-p")) { - const nxt = args.next(); + const nxt = arg_iter.next(); if (nxt == null or nxt.?.len == 0) { try out.print("-p must be followed by a number\n", .{}); return errors.InvalidArguments; diff --git a/src/c_decomp.zig b/src/c_decomp.zig index c16d810..0e5fdcf 100644 --- a/src/c_decomp.zig +++ b/src/c_decomp.zig @@ -5,17 +5,17 @@ const c = @import("c"); const Error = @import("decomp.zig").Error; pub fn zlibDecompress(_: std.mem.Allocator, in: []u8, out: []u8) Error!usize { - var strem: c.zng_stream = .{ + var strem: c.z_stream = .{ .next_in = in.ptr, .avail_in = @truncate(in.len), .next_out = out.ptr, .avail_out = @truncate(out.len), }; - var res = c.zng_inflateInit(&strem); + var res = c.inflateInit(&strem); if (res != c.Z_OK) return Error.ReadFailed; - defer _ = c.zng_inflateEnd(&strem); + defer _ = c.inflateEnd(&strem); - res = c.zng_inflate(&strem, c.Z_FULL_FLUSH); + res = c.inflate(&strem, c.Z_FULL_FLUSH); if (res != c.Z_OK) return Error.ReadFailed; return strem.total_out; diff --git a/src/decomp_cache.zig b/src/decomp_cache.zig index ad1ed0c..7e9c19e 100644 --- a/src/decomp_cache.zig +++ b/src/decomp_cache.zig @@ -19,7 +19,7 @@ cond: std.Io.Condition = .init, max_mem: u64, cur_mem: u64 = 0, -pub fn init(alloc: std.mem.Allocator, map: MemoryMap, compression: Decomp.Enum, max_mem: u64) DecompCache { +pub fn init(alloc: std.mem.Allocator, map: MemoryMap, compression: Decomp.Enum, max_mem: u64) !DecompCache { return .{ .alloc = alloc, .map = map, @@ -55,8 +55,8 @@ pub fn get(self: *DecompCache, io: Io, offset: u64, compressed_size: u32, max_si const cache = try self.cache.getOrPut(offset); if (cache.found_existing) { - _ = cache.?.usage.fetchAdd(1, .acquire); - return cache.?.data; + _ = cache.value_ptr.usage.fetchAdd(1, .acquire); + return cache.value_ptr.data; } errdefer self.cache.removeByPtr(cache.key_ptr); @@ -104,7 +104,7 @@ fn ensureSpace(self: *DecompCache, io: Io, size: u64) !void { } } if (self.cur_mem + size <= self.max_mem) return; - try self.cond.wait(io, self.mut.mutex); + try self.cond.wait(io, &self.mut.mutex); } } diff --git a/src/directory.zig b/src/directory.zig index 9d2a644..7eee8f8 100644 --- a/src/directory.zig +++ b/src/directory.zig @@ -46,7 +46,7 @@ pub fn init(alloc: std.mem.Allocator, rdr: *Reader, size: u32) !Directory { } } - return entries.toOwnedSlice(alloc); + return .{ .entries = try entries.toOwnedSlice(alloc) }; } pub fn deinit(self: Directory, alloc: std.mem.Allocator) void { for (self.entries) |entry| diff --git a/src/extract.zig b/src/extract.zig new file mode 100644 index 0000000..914cab2 --- /dev/null +++ b/src/extract.zig @@ -0,0 +1,51 @@ +const std = @import("std"); +const Io = std.Io; + +const DecompCache = @import("decomp_cache.zig"); +const ExtractionOptions = @import("options.zig"); +const Inode = @import("inode.zig"); +const Superblock = @import("archive.zig").Superblock; + +pub fn extract(alloc: std.mem.Allocator, io: Io, inode: Inode, cache: *DecompCache, super: Superblock, ext_loc: []const u8, options: ExtractionOptions) !void { + _ = alloc; + _ = io; + _ = inode; + _ = cache; + _ = super; + _ = ext_loc; + _ = options; + return error.TODO; +} + +pub fn extractDir(alloc: std.mem.Allocator, io: Io, path: []const u8, d: anytype) Error!PathReturn {} +pub fn extractFile(alloc: std.mem.Allocator, io: Io, path: []const u8, d: anytype) Error!PathReturn { + const atomic = try Io.Dir.cwd().createFileAtomic(io, path, .{}); + defer atomic.deinit(io); + + // TODO + + try atomic.link(io); + // return .{ + // .path = path, + // }; + return error.TODO; +} + +// Utility types + +const ReturnUnion = union { + path_ret: Error!PathReturn, +}; + +const Error = error{}; + +const PathReturn = struct { + path: []const u8, + + uid_idx: u32, + gid_idx: u32, + mod_time: u32, + permission: u16, + + xattr_idx: ?u32, +}; diff --git a/src/file.zig b/src/file.zig index 9f79e87..f71386b 100644 --- a/src/file.zig +++ b/src/file.zig @@ -1,3 +1,11 @@ +const std = @import("std"); +const Io = std.Io; + +const Archive = @import("archive.zig"); +const Directory = @import("directory.zig"); +const ExtractionOptions = @import("options.zig"); +const Inode = @import("inode.zig"); + const SfsFile = @This(); alloc: std.mem.Allocator, @@ -28,7 +36,7 @@ pub fn initDirEntry(alloc: std.mem.Allocator, io: Io, archive: *Archive, entry: .inode = try .initDirEntry( alloc, io, - archive.cache, + &archive.cache, archive.super.inode_start, archive.super.block_size, entry, @@ -61,7 +69,7 @@ pub fn open(self: SfsFile, alloc: std.mem.Allocator, io: Io, filepath: []const u const first_element: []const u8 = std.mem.sliceTo(path, '/'); - const dir: Directory = try self.inode.directory(alloc, io, self.archive.cache, self.archive.super.dir_start); + const dir: Directory = try self.inode.directory(alloc, io, &self.archive.cache, self.archive.super.dir_start); defer dir.deinit(alloc); var cur_slice = dir.entries; @@ -76,7 +84,7 @@ pub fn open(self: SfsFile, alloc: std.mem.Allocator, io: Io, filepath: []const u } else { return error.NotFound; } - if (first_element.len == path) return .initDirEntry(alloc, io, self.archive, cur_slice[idx]); + if (first_element.len == path.len) return .initDirEntry(alloc, io, self.archive, cur_slice[idx]); if (cur_slice[idx].type != .dir) return error.NotFound; const tmp_file: SfsFile = try .initDirEntry(alloc, io, self.archive, cur_slice[idx]); defer tmp_file.deinit(); @@ -84,9 +92,11 @@ pub fn open(self: SfsFile, alloc: std.mem.Allocator, io: Io, filepath: []const u return tmp_file.open(alloc, io, path[first_element.len..]); } -const std = @import("std"); -const Io = std.Io; - -const Archive = @import("archive.zig"); -const Directory = @import("directory.zig"); -const Inode = @import("inode.zig"); +pub fn extract(self: SfsFile, alloc: std.mem.Allocator, io: Io, ext_dir: []const u8, options: ExtractionOptions) !void { + _ = self; + _ = alloc; + _ = io; + _ = ext_dir; + _ = options; + return error.TODO; +} diff --git a/src/inode.zig b/src/inode.zig index 7a47617..7fefbf1 100644 --- a/src/inode.zig +++ b/src/inode.zig @@ -280,7 +280,7 @@ const ExtFile = struct { .blocks = blocks, }; } - pub fn deinit(self: File, alloc: std.mem.Allocator) void { + pub fn deinit(self: ExtFile, alloc: std.mem.Allocator) void { alloc.free(self.blocks); } }; @@ -342,7 +342,7 @@ const ExtSymlink = struct { }; } - pub fn deinit(self: Symlink, alloc: std.mem.Allocator) void { + pub fn deinit(self: ExtSymlink, alloc: std.mem.Allocator) void { alloc.free(self.target); } };