diff --git a/build.zig b/build.zig new file mode 100644 index 0000000..e720f30 --- /dev/null +++ b/build.zig @@ -0,0 +1,23 @@ +const std = @import("std"); + +pub fn build(b: *std.Build) void { + const target = b.standardTargetOptions(.{}); + const optimize = b.standardOptimizeOption(.{}); + const lib_mod = b.createModule(.{ + .root_source_file = b.path("src/root.zig"), + .target = target, + .optimize = optimize, + }); + const lib = b.addLibrary(.{ + .linkage = .static, + .name = "zig_squashfs", + .root_module = lib_mod, + }); + b.installArtifact(lib); + const lib_unit_tests = b.addTest(.{ + .root_module = lib_mod, + }); + const run_lib_unit_tests = b.addRunArtifact(lib_unit_tests); + const test_step = b.step("test", "Run unit tests"); + test_step.dependOn(&run_lib_unit_tests.step); +} diff --git a/build.zig.zon b/build.zig.zon new file mode 100644 index 0000000..e676982 --- /dev/null +++ b/build.zig.zon @@ -0,0 +1,61 @@ +.{ + .name = .zig_squashfs, + .version = "0.0.0", + .fingerprint = 0x527960c72c03ffe3, // Changing this has security and trust implications. + + .minimum_zig_version = "0.14.0", + + // This field is optional. + // Each dependency must either provide a `url` and `hash`, or a `path`. + // `zig build --fetch` can be used to fetch all dependencies of a package, recursively. + // Once all dependencies are fetched, `zig build` no longer requires + // internet connectivity. + .dependencies = .{ + // See `zig fetch --save ` for a command-line interface for adding dependencies. + //.example = .{ + // // When updating this field to a new URL, be sure to delete the corresponding + // // `hash`, otherwise you are communicating that you expect to find the old hash at + // // the new URL. If the contents of a URL change this will result in a hash mismatch + // // which will prevent zig from using it. + // .url = "https://example.com/foo.tar.gz", + // + // // This is computed from the file contents of the directory of files that is + // // obtained after fetching `url` and applying the inclusion rules given by + // // `paths`. + // // + // // This field is the source of truth; packages do not come from a `url`; they + // // come from a `hash`. `url` is just one of many possible mirrors for how to + // // obtain a package matching this `hash`. + // // + // // Uses the [multihash](https://multiformats.io/multihash/) format. + // .hash = "...", + // + // // When this is provided, the package is found in a directory relative to the + // // build root. In this case the package's hash is irrelevant and therefore not + // // computed. This field and `url` are mutually exclusive. + // .path = "foo", + // + // // When this is set to `true`, a package is declared to be lazily + // // fetched. This makes the dependency only get fetched if it is + // // actually used. + // .lazy = false, + //}, + }, + + // Specifies the set of files and directories that are included in this package. + // Only files and directories listed here are included in the `hash` that + // is computed for this package. Only files listed here will remain on disk + // when using the zig package manager. As a rule of thumb, one should list + // files required for compilation plus any license(s). + // Paths are relative to the build root. Use the empty string (`""`) to refer to + // the build root itself. + // A directory listed here means that all files within, recursively, are included. + .paths = .{ + "build.zig", + "build.zig.zon", + "src", + // For example... + //"LICENSE", + //"README.md", + }, +} diff --git a/file.zig b/file.zig deleted file mode 100644 index 1fac5a8..0000000 --- a/file.zig +++ /dev/null @@ -1,14 +0,0 @@ -const inode = @import("inode.zig"); -const Reader = @import("squashfs.zig").Reader; -const MetadataReader = @import("metadata_reader.zig").MetadataReader; - -pub const File = struct { - rdr: *Reader, - inode: inode.Inode, - name: []const u8, - dir_entries: []const void = undefined, //TODO - - pub fn fromRef(ref: inode.InodeRef, rdr: *Reader) !File { - var meta_rdr: MetadataReader = .init(rdr.super.comp, rdr: io.AnyReader, alloc: std.mem.Allocator) - } -}; diff --git a/file_offset_reader.zig b/file_offset_reader.zig deleted file mode 100644 index 0d0017b..0000000 --- a/file_offset_reader.zig +++ /dev/null @@ -1,7 +0,0 @@ -const std = @import("std"); - -const FileOffsetReader = struct { - file: std.fs.File, - - pub fn any(self: *FileOffsetReader) !std.io.AnyReader {} -}; diff --git a/decompress.zig b/src/decompress.zig similarity index 100% rename from decompress.zig rename to src/decompress.zig diff --git a/src/directory.zig b/src/directory.zig new file mode 100644 index 0000000..fa767fe --- /dev/null +++ b/src/directory.zig @@ -0,0 +1,45 @@ +const std = @import("std"); + +const DirHeader = packed struct { + count: u32, + inode_block_start: u32, + inode_num: u32, +}; + +const RawDirEntry = struct { + inode_offset: u16, + inode_num_difference: i16, + inode_type: u16, + name_size: u16, + name: []u8, + + fn init(rdr: std.io.AnyReader, alloc: std.mem.Allocator) !DirEntry { + var out: DirEntry = .{ + .inode_offset = try rdr.readInt(u16, std.builtin.Endian.little), + .inode_num_difference = try rdr.readInt(i16, std.builtin.Endian.little), + .inode_type = try rdr.readInt(u16, std.builtin.Endian.little), + .name_size = try rdr.readInt(u16, std.builtin.Endian.little), + .name = undefined, + }; + out.name = try alloc.alloc(u8, out.name_size); + _ = try rdr.readAll(out.name); + return out; + } +}; + +pub const DirEntry = struct { + inode_offset: u16, + inode_block_start: u32, + inode_num: u32, + name: []u8, + + fn init(raw: RawDirEntry, hdr: DirHeader) DirEntry { + return .{ + .inode_offset = raw.inode_offset, + .inode_block_start = hdr.inode_block_start, + .inode_num = hdr.inode_num - raw.inode_num_difference, + .name = raw.name, + }; + } +}; + diff --git a/src/file.zig b/src/file.zig new file mode 100644 index 0000000..2f09441 --- /dev/null +++ b/src/file.zig @@ -0,0 +1,23 @@ +const inode = @import("inode.zig"); +const Reader = @import("squashfs.zig").Reader; +const MetadataReader = @import("metadata_reader.zig").MetadataReader; +const FileOffsetReader = @import("file_offset_reader.zig").FileOffsetReader; + +pub const File = struct { + rdr: *Reader, + inode: inode.Inode, + name: []const u8, + dir_entries: []const void = undefined, //TODO + + pub fn fromRef(ref: inode.InodeRef, name: []const u8, rdr: *Reader) !File { + var offset_rdr: FileOffsetReader = .init(rdr.file, rdr.super.inode_table + ref.block_start); + var meta_rdr: MetadataReader = .init(rdr.super.comp, offset_rdr.any(), rdr.alloc.allocator()); + try meta_rdr.skip(ref.offset); + const in = try inode.readInode(meta_rdr, rdr.super.block_size, rdr.alloc.allocator()); + return .{ + .rdr = rdr, + .inode = in, + .name = name, + }; + } +}; diff --git a/src/file_offset_reader.zig b/src/file_offset_reader.zig new file mode 100644 index 0000000..baf1168 --- /dev/null +++ b/src/file_offset_reader.zig @@ -0,0 +1,31 @@ +const std = @import("std"); + +pub const FileOffsetReader = struct { + file: std.fs.File, + offset: u64, + + pub fn init(file: std.fs.File, initial_offset: u64) FileOffsetReader { + return .{ + .file = file, + .offset = initial_offset, + }; + } + + pub fn read(self: *FileOffsetReader, bytes: []u8) anyerror!usize { + const red = try self.file.preadAll(bytes, self.offset); + self.offset += @intCast(red); + return red; + } + + pub fn any(self: *FileOffsetReader) std.io.AnyReader { + return .{ + .context = @ptrCast(self), + .readFn = readOpaque, + }; + } + + fn readOpaque(context: *const anyopaque, buf: []u8) anyerror!usize { + var self: *FileOffsetReader = @constCast(@ptrCast(@alignCast(context))); + return self.read(buf); + } +}; diff --git a/inode.zig b/src/inode.zig similarity index 100% rename from inode.zig rename to src/inode.zig diff --git a/inode_types/dir.zig b/src/inode_types/dir.zig similarity index 100% rename from inode_types/dir.zig rename to src/inode_types/dir.zig diff --git a/inode_types/file.zig b/src/inode_types/file.zig similarity index 100% rename from inode_types/file.zig rename to src/inode_types/file.zig diff --git a/inode_types/misc.zig b/src/inode_types/misc.zig similarity index 100% rename from inode_types/misc.zig rename to src/inode_types/misc.zig diff --git a/inode_types/sym.zig b/src/inode_types/sym.zig similarity index 100% rename from inode_types/sym.zig rename to src/inode_types/sym.zig diff --git a/metadata_reader.zig b/src/metadata_reader.zig similarity index 100% rename from metadata_reader.zig rename to src/metadata_reader.zig diff --git a/squashfs.zig b/src/squashfs.zig similarity index 63% rename from squashfs.zig rename to src/squashfs.zig index 055487a..8bb3118 100644 --- a/squashfs.zig +++ b/src/squashfs.zig @@ -5,6 +5,7 @@ const Superblock = @import("superblock.zig").Superblock; const inode = @import("inode.zig"); const MetadataReader = @import("metadata_reader.zig").MetadataReader; const File = @import("file.zig").File; +const FileOffsetReader = @import("file_offset_reader.zig").FileOffsetReader; pub const Reader = struct { super: Superblock, @@ -25,15 +26,24 @@ pub fn newReader(filename: []const u8) !Reader { errdefer _ = alloc.deinit(); const super = try file.reader().readStruct(Superblock); try super.valid(); - try file.seekTo(super.inode_table + super.root_inode.block_start); - var root_reader: MetadataReader = try .init(super.comp, file.reader().any(), alloc.allocator()); + var offset_rdr: FileOffsetReader = .init(file, super.inode_table + super.root_inode.block_start); + var root_reader: MetadataReader = try .init( + super.comp, + offset_rdr.any(), + alloc.allocator(), + ); defer root_reader.deinit(); try root_reader.skip(super.root_inode.offset); - const root_inode = try inode.readInode(root_reader.any(), super.block_size, alloc.allocator()); - return Reader{ + var out: Reader = undefined; + out = Reader{ .super = super, .rdr = file, - .root = root_inode, + .root = .{ + .inode = try inode.readInode(root_reader.any(), super.block_size, alloc.allocator()), + .name = "", + .rdr = &out, + }, .alloc = alloc, }; + return out; } diff --git a/superblock.zig b/src/superblock.zig similarity index 100% rename from superblock.zig rename to src/superblock.zig diff --git a/test_squashfs.zig b/src/test_squashfs.zig similarity index 100% rename from test_squashfs.zig rename to src/test_squashfs.zig