297 lines
8.3 KiB
Zig
297 lines
8.3 KiB
Zig
const std = @import("std");
|
|
const Io = std.Io;
|
|
const Reader = Io.Reader;
|
|
|
|
const Decomp = @import("decomp.zig");
|
|
const Directory = @import("directory.zig");
|
|
const MetadataReader = @import("meta_rdr.zig");
|
|
const Superblock = @import("archive.zig").Superblock;
|
|
const Extract = @import("extract.zig");
|
|
const ExtractionOptions = @import("options.zig");
|
|
|
|
const Inode = @This();
|
|
|
|
hdr: Header,
|
|
data: Data,
|
|
|
|
pub fn init(alloc: std.mem.Allocator, block_size: u32, rdr: *Reader) !Inode {
|
|
var hdr: Header = undefined;
|
|
try rdr.readSliceEndian(Header, @ptrCast(&hdr), .little);
|
|
|
|
const data: Data = switch (hdr.type) {
|
|
.dir => .{ .dir = try .init(rdr) },
|
|
.ext_dir => .{ .ext_dir = try .init(rdr) },
|
|
.file => .{ .file = try .init(alloc, rdr, block_size) },
|
|
.ext_file => .{ .ext_file = try .init(alloc, rdr, block_size) },
|
|
.symlink => .{ .symlink = try .init(alloc, rdr) },
|
|
.ext_symlink => .{ .ext_symlink = try .init(alloc, rdr) },
|
|
.block_dev => .{ .block_dev = try .init(rdr) },
|
|
.ext_block_dev => .{ .ext_block_dev = try .init(rdr) },
|
|
.char_dev => .{ .char_dev = try .init(rdr) },
|
|
.ext_char_dev => .{ .ext_char_dev = try .init(rdr) },
|
|
.fifo => .{ .fifo = try .init(rdr) },
|
|
.ext_fifo => .{ .ext_fifo = try .init(rdr) },
|
|
.socket => .{ .socket = try .init(rdr) },
|
|
.ext_socket => .{ .ext_socket = try .init(rdr) },
|
|
};
|
|
return .{ .hdr = hdr, .data = data };
|
|
}
|
|
pub fn initRef(alloc: std.mem.Allocator, data: []u8, decomp: Decomp.Fn, inode_start: u64, block_size: u32, ref: Ref) !Inode {
|
|
var meta: MetadataReader = .init(alloc, data, decomp, inode_start + ref.block_start);
|
|
try meta.interface.discardAll(ref.block_offset);
|
|
|
|
return .init(alloc, block_size, &meta.interface);
|
|
}
|
|
pub fn initEntry(alloc: std.mem.Allocator, data: []u8, decomp: Decomp.Fn, inode_start: u64, block_size: u32, entry: Directory.Entry) !Inode {
|
|
var meta: MetadataReader = .init(alloc, data, decomp, inode_start + entry.block_start);
|
|
try meta.interface.discardAll(entry.block_offset);
|
|
|
|
return .init(alloc, block_size, &meta.interface);
|
|
}
|
|
pub fn deinit(self: Inode, alloc: std.mem.Allocator) void {
|
|
switch (self.data) {
|
|
.file => |f| alloc.free(f.blocks),
|
|
.ext_file => |f| alloc.free(f.blocks),
|
|
.symlink => |s| alloc.free(s.target),
|
|
.ext_symlink => |s| alloc.free(s.target),
|
|
else => {},
|
|
}
|
|
}
|
|
|
|
pub fn extract(self: Inode, alloc: std.mem.Allocator, io: Io, super: Superblock, data: []u8, decomp: Decomp.Fn, location: []const u8, options: ExtractionOptions) !void {
|
|
return Extract.extract(alloc, io, super, data, decomp, self, location, options);
|
|
}
|
|
|
|
// Types
|
|
|
|
pub const Ref = packed struct(u64) {
|
|
block_offset: u16,
|
|
block_start: u32,
|
|
_: u16,
|
|
};
|
|
|
|
pub const Type = enum(u16) {
|
|
dir = 1,
|
|
file,
|
|
symlink,
|
|
block_dev,
|
|
char_dev,
|
|
fifo,
|
|
socket,
|
|
ext_dir,
|
|
ext_file,
|
|
ext_symlink,
|
|
ext_block_dev,
|
|
ext_char_dev,
|
|
ext_fifo,
|
|
ext_socket,
|
|
};
|
|
|
|
pub const Header = extern struct {
|
|
type: Type,
|
|
permissions: u16,
|
|
uid_idx: u16,
|
|
gid_idx: u16,
|
|
mod_time: u32,
|
|
num: u32,
|
|
};
|
|
|
|
pub const Data = union(Type) {
|
|
dir: Dir,
|
|
file: File,
|
|
symlink: Symlink,
|
|
block_dev: Dev,
|
|
char_dev: Dev,
|
|
fifo: IPC,
|
|
socket: IPC,
|
|
ext_dir: ExtDir,
|
|
ext_file: ExtFile,
|
|
ext_symlink: ExtSymlink,
|
|
ext_block_dev: ExtDev,
|
|
ext_char_dev: ExtDev,
|
|
ext_fifo: ExtIPC,
|
|
ext_socket: ExtIPC,
|
|
};
|
|
|
|
pub const DataBlock = packed struct(u32) {
|
|
size: u24,
|
|
uncompressed: bool,
|
|
_: u7,
|
|
};
|
|
|
|
pub const Dir = extern struct {
|
|
block_start: u32,
|
|
hard_links: u32,
|
|
size: u16,
|
|
block_offset: u16,
|
|
parent_num: u32,
|
|
|
|
fn init(rdr: *Reader) !Dir {
|
|
var new: Dir = undefined;
|
|
try rdr.readSliceEndian(Dir, @ptrCast(&new), .little);
|
|
return new;
|
|
}
|
|
};
|
|
pub const ExtDir = extern struct {
|
|
hard_links: u32,
|
|
size: u32,
|
|
block_start: u32,
|
|
parent_num: u32,
|
|
idx_count: u16,
|
|
block_offset: u16,
|
|
xattr_idx: u32,
|
|
|
|
fn init(rdr: *Reader) !ExtDir {
|
|
var new: ExtDir = undefined;
|
|
try rdr.readSliceEndian(ExtDir, @ptrCast(&new), .little);
|
|
return new;
|
|
}
|
|
};
|
|
pub const File = struct {
|
|
block_start: u32,
|
|
frag_idx: u32,
|
|
frag_offset: u32,
|
|
size: u32,
|
|
blocks: []DataBlock,
|
|
|
|
fn init(alloc: std.mem.Allocator, rdr: *Reader, block_size: u32) !File {
|
|
var data: [16]u8 = undefined;
|
|
try rdr.readSliceAll(&data);
|
|
|
|
const frag_idx = std.mem.readInt(u32, data[4..8], .little);
|
|
const size = std.mem.readInt(u32, data[12..], .little);
|
|
|
|
var blocks_num = size / block_size;
|
|
if (size % block_size != 0 and frag_idx == 0xFFFFFFFF)
|
|
blocks_num += 1;
|
|
|
|
const blocks = try alloc.alloc(DataBlock, blocks_num);
|
|
errdefer alloc.free(blocks);
|
|
try rdr.readSliceEndian(DataBlock, blocks, .little);
|
|
|
|
return .{
|
|
.block_start = std.mem.readInt(u32, data[0..4], .little),
|
|
.frag_idx = frag_idx,
|
|
.frag_offset = std.mem.readInt(u32, data[8..12], .little),
|
|
.size = size,
|
|
.blocks = blocks,
|
|
};
|
|
}
|
|
};
|
|
pub const ExtFile = struct {
|
|
block_start: u64,
|
|
size: u64,
|
|
sparse: u64,
|
|
hard_links: u32,
|
|
frag_idx: u32,
|
|
frag_offset: u32,
|
|
xattr_idx: u32,
|
|
blocks: []DataBlock,
|
|
|
|
fn init(alloc: std.mem.Allocator, rdr: *Reader, block_size: u32) !ExtFile {
|
|
var data: [40]u8 = undefined;
|
|
try rdr.readSliceAll(&data);
|
|
|
|
const frag_idx = std.mem.readInt(u32, data[28..32], .little);
|
|
const size = std.mem.readInt(u64, data[8..16], .little);
|
|
|
|
var blocks_num = size / block_size;
|
|
if (size % block_size != 0 and frag_idx == 0xFFFFFFFF)
|
|
blocks_num += 1;
|
|
|
|
const blocks = try alloc.alloc(DataBlock, blocks_num);
|
|
errdefer alloc.free(blocks);
|
|
try rdr.readSliceEndian(DataBlock, blocks, .little);
|
|
|
|
return .{
|
|
.block_start = std.mem.readInt(u64, data[0..8], .little),
|
|
.size = size,
|
|
.sparse = std.mem.readInt(u64, data[16..24], .little),
|
|
.hard_links = std.mem.readInt(u32, data[24..28], .little),
|
|
.frag_idx = frag_idx,
|
|
.frag_offset = std.mem.readInt(u32, data[32..36], .little),
|
|
.xattr_idx = std.mem.readInt(u32, data[36..], .little),
|
|
.blocks = blocks,
|
|
};
|
|
}
|
|
};
|
|
pub const Symlink = struct {
|
|
hard_links: u32,
|
|
target: []const u8,
|
|
|
|
fn init(alloc: std.mem.Allocator, rdr: *Reader) !Symlink {
|
|
var data: [8]u8 = undefined;
|
|
try rdr.readSliceAll(&data);
|
|
|
|
const target_size = std.mem.readInt(u32, data[4..], .little);
|
|
|
|
const target = try alloc.alloc(u8, target_size);
|
|
errdefer alloc.free(target);
|
|
try rdr.readSliceEndian(u8, target, .little);
|
|
|
|
return .{
|
|
.hard_links = std.mem.readInt(u32, data[0..4], .little),
|
|
.target = target,
|
|
};
|
|
}
|
|
};
|
|
pub const ExtSymlink = struct {
|
|
hard_links: u32,
|
|
target: []const u8,
|
|
xattr_idx: u32,
|
|
|
|
fn init(alloc: std.mem.Allocator, rdr: *Reader) !ExtSymlink {
|
|
const sym: Symlink = try .init(alloc, rdr);
|
|
errdefer alloc.free(sym.target);
|
|
|
|
var xattr_idx: u32 = undefined;
|
|
try rdr.readSliceEndian(u32, @ptrCast(&xattr_idx), .little);
|
|
|
|
return .{
|
|
.hard_links = sym.hard_links,
|
|
.target = sym.target,
|
|
.xattr_idx = xattr_idx,
|
|
};
|
|
}
|
|
};
|
|
pub const Dev = extern struct {
|
|
hard_links: u32,
|
|
device: u32,
|
|
|
|
fn init(rdr: *Reader) !Dev {
|
|
var new: Dev = undefined;
|
|
try rdr.readSliceEndian(Dev, @ptrCast(&new), .little);
|
|
return new;
|
|
}
|
|
};
|
|
pub const ExtDev = extern struct {
|
|
hard_links: u32,
|
|
device: u32,
|
|
xattr_idx: u32,
|
|
|
|
fn init(rdr: *Reader) !ExtDev {
|
|
var new: ExtDev = undefined;
|
|
try rdr.readSliceEndian(ExtDev, @ptrCast(&new), .little);
|
|
return new;
|
|
}
|
|
};
|
|
pub const IPC = extern struct {
|
|
hard_links: u32,
|
|
|
|
fn init(rdr: *Reader) !IPC {
|
|
var new: IPC = undefined;
|
|
try rdr.readSliceEndian(IPC, @ptrCast(&new), .little);
|
|
return new;
|
|
}
|
|
};
|
|
pub const ExtIPC = extern struct {
|
|
hard_links: u32,
|
|
xattr_idx: u32,
|
|
|
|
fn init(rdr: *Reader) !ExtIPC {
|
|
var new: ExtIPC = undefined;
|
|
try rdr.readSliceEndian(ExtIPC, @ptrCast(&new), .little);
|
|
return new;
|
|
}
|
|
};
|