Restart (once again)
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
//! A squashfs archive read from a file.
|
||||
//! Can be used to directly access File's contents or extract to the filesystem.
|
||||
|
||||
const std = @import("std");
|
||||
const File = std.fs.File;
|
||||
|
||||
const Superblock = @import("super.zig").Superblock;
|
||||
const OffsetFile = @import("util/offset_file.zig");
|
||||
|
||||
const Archive = @This();
|
||||
|
||||
// 4 Gigs
|
||||
const MIN_MEM_SIZE = 4 * 1024 * 1024 * 1024;
|
||||
|
||||
parent_alloc: std.mem.Allocator,
|
||||
alloc: std.heap.FixedBufferAllocator,
|
||||
fixed_buf: []u8,
|
||||
fil: OffsetFile,
|
||||
|
||||
super: Superblock,
|
||||
|
||||
/// Default settings using std.Thread.getCpuCount() threads and the minimum of 4gb or half of system memory for memory usage.
|
||||
pub fn init(alloc: std.mem.Allocator, fil: File) !Archive {
|
||||
return initAdvanced(
|
||||
alloc,
|
||||
fil,
|
||||
0,
|
||||
try std.Thread.getCpuCount(),
|
||||
@min(MIN_MEM_SIZE, std.process.totalSystemMemory() / 2),
|
||||
);
|
||||
}
|
||||
/// Create the Archive dictating the amount of threads & memory used.
|
||||
/// If trying to extract a full archive, a large memory size & thread count could help.
|
||||
/// If you're planning on only interacting with a small number of files, it should be fine to use few threads and a small memory size.
|
||||
pub fn initAdvanced(alloc: std.mem.Allocator, fil: File, offset: u64, threads: usize, mem: usize) !Archive {
|
||||
_ = threads;
|
||||
var super: Superblock = undefined;
|
||||
const red = try fil.pread(@ptrCast(&super), offset);
|
||||
std.debug.assert(red == @sizeOf(Superblock));
|
||||
const fixed_buf = alloc.alloc(u8, mem);
|
||||
return .{
|
||||
.parent_alloc = alloc,
|
||||
.alloc = .init(fixed_buf),
|
||||
.fixed_buf = fixed_buf,
|
||||
.fil = .init(fil, offset),
|
||||
|
||||
.super = super,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Archive) void {
|
||||
self.parent_alloc.free(self.fixed_buf);
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
const std = @import("std");
|
||||
|
||||
const squashfs = @import("zig_squashfs");
|
||||
|
||||
pub fn main() !void {}
|
||||
@@ -0,0 +1,11 @@
|
||||
const std = @import("std");
|
||||
const compress = std.compress;
|
||||
|
||||
pub const CompressionType = enum(u16) {
|
||||
gzig = 1,
|
||||
lzma,
|
||||
lzo,
|
||||
xz,
|
||||
lz4,
|
||||
zstd,
|
||||
};
|
||||
@@ -0,0 +1,5 @@
|
||||
pub const Ref = packed struct {
|
||||
_: u16,
|
||||
table_offset: u32,
|
||||
block_offset: u16,
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
const Archive = @import("archive.zig");
|
||||
@@ -0,0 +1,62 @@
|
||||
const math = @import("std").math;
|
||||
|
||||
const CompressionType = @import("decomp.zig").CompressionType;
|
||||
const InodeRef = @import("inode.zig").Ref;
|
||||
|
||||
const SQUASHFS_MAGIC: u32 = "hsqs";
|
||||
|
||||
const SuperblockError = error{
|
||||
InvalidMagic,
|
||||
InvalidBlockLog,
|
||||
InvalidVersion,
|
||||
InvalidCheck,
|
||||
};
|
||||
|
||||
/// A squashfs Superblock
|
||||
pub const Superblock = packed struct {
|
||||
magic: u32,
|
||||
inode_count: u32,
|
||||
mod_time: u32,
|
||||
block_size: u32,
|
||||
frag_count: u32,
|
||||
compression: CompressionType,
|
||||
block_log: u16,
|
||||
flags: packed struct {
|
||||
inode_uncompressed: bool,
|
||||
data_uncompressed: bool,
|
||||
check: bool,
|
||||
frag_uncompressed: bool,
|
||||
fragment_never: bool,
|
||||
fragment_always: bool,
|
||||
duplicates: bool,
|
||||
exportable: bool,
|
||||
xattr_uncompressed: bool,
|
||||
xattr_never: bool,
|
||||
compression_options: bool,
|
||||
ids_uncompressed: bool,
|
||||
_: u4,
|
||||
},
|
||||
id_count: u16,
|
||||
ver_maj: u16,
|
||||
ver_min: u16,
|
||||
root_ref: InodeRef,
|
||||
size: u64,
|
||||
id_start: u64,
|
||||
xattr_start: u64,
|
||||
inode_start: u64,
|
||||
dir_start: u64,
|
||||
frag_start: u64,
|
||||
export_start: u64,
|
||||
|
||||
/// Validate the Superblock. If an error is returned, it's likely the archive is corrupted or not a squashfs archive.
|
||||
pub fn validate(self: Superblock) !void {
|
||||
if (self.magic != SQUASHFS_MAGIC)
|
||||
return SuperblockError.InvalidMagic;
|
||||
if (self.magic.flags.check)
|
||||
return SuperblockError.InvalidCheck;
|
||||
if (self.ver_maj != 4 or self.ver_min != 0)
|
||||
return SuperblockError.InvalidVersion;
|
||||
if (math.log2(self.block_size) != self.block_log)
|
||||
return SuperblockError.InvalidBlockLog;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,72 @@
|
||||
const std = @import("std");
|
||||
const Mutex = std.Thread.Mutex;
|
||||
|
||||
const DecompMgr = @import("decomp.zig");
|
||||
const MetadataReader = @import("util/metadata.zig");
|
||||
const OffsetFile = @import("util/offset_file.zig");
|
||||
|
||||
const TableError = error{
|
||||
InvalidIndex,
|
||||
};
|
||||
|
||||
pub fn Table(T: anytype) type {
|
||||
return struct {
|
||||
const This = @This();
|
||||
|
||||
const VALS_PER_BLOCK = 8192 / @sizeOf(T);
|
||||
|
||||
alloc: std.mem.Allocator,
|
||||
fil: OffsetFile,
|
||||
decomp: *DecompMgr,
|
||||
tab_start: u64,
|
||||
|
||||
mut: Mutex = .{},
|
||||
|
||||
tab: std.AutoHashMap(u32, []T),
|
||||
values: u32,
|
||||
|
||||
pub fn init(alloc: std.mem.Allocator, fil: OffsetFile, decomp: *DecompMgr, tab_start: u64, values: u32) !This {
|
||||
return .{
|
||||
.alloc = alloc,
|
||||
.fil = fil,
|
||||
.decomp = decomp,
|
||||
.tab_start = tab_start,
|
||||
|
||||
.tab = .init(alloc),
|
||||
.values = values,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *This) void {
|
||||
self.tab.deinit();
|
||||
}
|
||||
|
||||
pub fn get(self: *This, idx: u32) !T {
|
||||
if (idx >= self.values) return TableError.InvalidIndex;
|
||||
const block_num = idx / VALS_PER_BLOCK;
|
||||
const idx_offset = idx - (block_num * VALS_PER_BLOCK);
|
||||
if (self.tab.contains(block_num)) {
|
||||
const block = self.tab.get(block_num).?;
|
||||
return block[idx_offset];
|
||||
}
|
||||
self.mut.lock();
|
||||
defer self.mut.unlock();
|
||||
// Double check in case of race condition..
|
||||
if (self.tab.contains(block_num)) {
|
||||
const block = self.tab.get(block_num).?;
|
||||
return block[idx_offset];
|
||||
}
|
||||
const is_last = (self.values - 1) / VALS_PER_BLOCK == block_num;
|
||||
const slice_size = if (is_last) self.values - (block_num * VALS_PER_BLOCK) else VALS_PER_BLOCK;
|
||||
const slice = try self.alloc.alloc(slice_size);
|
||||
var rdr = try self.fil.readerAt(self.tab_start + (8 * block_num), &[0]u8{});
|
||||
const offset: u64 = 0;
|
||||
try rdr.interface.readSliceAll(@ptrCast(&idx_offset));
|
||||
rdr = try self.fil.readerAt(offset, &[0]u8{});
|
||||
var meta: MetadataReader = .init(&rdr.interface, self.decomp);
|
||||
try meta.interface.readSliceAll(@ptrCast(slice));
|
||||
//TODO: read & decompress block.
|
||||
self.tab.put(block_num, slice);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
const std = @import("std");
|
||||
const stuff = @import("builtin");
|
||||
|
||||
test "Basics" {}
|
||||
|
||||
test "ExtractSingleFile" {}
|
||||
|
||||
test "ExtractCompleteArchive" {}
|
||||
@@ -0,0 +1,34 @@
|
||||
const std = @import("std");
|
||||
const Reader = std.Io.Reader;
|
||||
const Writer = std.Io.Writer;
|
||||
const Limit = std.Io.Limit;
|
||||
const StreamError = std.Io.Reader.StreamError;
|
||||
|
||||
const DecompMgr = @import("../decomp.zig");
|
||||
|
||||
const This = @This();
|
||||
|
||||
rdr: Reader,
|
||||
decomp: *DecompMgr,
|
||||
|
||||
buf: [8192]u8 = undefined,
|
||||
|
||||
interface: Reader,
|
||||
|
||||
pub fn init(rdr: Reader, decomp: *DecompMgr) This {
|
||||
return .{
|
||||
.rdr = rdr,
|
||||
.decomp = decomp,
|
||||
.interface = .{
|
||||
.buffer = &[0]u8{},
|
||||
.end = 0,
|
||||
.seek = 0,
|
||||
.vtable = &{
|
||||
.stream = stream,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
fn stream(rdr: *Reader, wrt: *Writer, limit: Limit) StreamError!usize{
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
//! A File that's meant where it's meaningful content starts at a given offset.
|
||||
|
||||
const std = @import("std");
|
||||
const File = std.fs.File;
|
||||
const Reader = std.fs.File.Reader;
|
||||
|
||||
const OffsetFile = @This();
|
||||
|
||||
fil: File,
|
||||
offset: u64,
|
||||
|
||||
pub fn init(fil: File, init_offset: u64) OffsetFile {
|
||||
return .{ .fil = fil, .offset = init_offset };
|
||||
}
|
||||
|
||||
pub fn readerAt(self: OffsetFile, offset: u64, buffer: []u8) !Reader {
|
||||
var rdr = self.fil.reader(buffer);
|
||||
try rdr.seekTo(self.offset + offset);
|
||||
return rdr;
|
||||
}
|
||||
Reference in New Issue
Block a user