Skip to documentation
SLOP

tiny.sandbox.scan

Reference tiny.sandbox scan

Defined in tiny.sandbox.

API (5)

Actions

Public operations.

Types and contracts

Public types and contracts.

No direct callersNo direct callstiny.sandboxscan
Static calls · unresolved targets: unknown · external targets: unknown.

Source

Called byCallscopiedexecutetest sourcelib.sandbox.src.layertest: layer preserves source symlinkstest sourcelib.sandbox.src.scantest: scan captures files directories...SnapshotappendSnapshotdeinitSnapshotinitSnapshotsortprivate sourcelib.sandbox.src.scanappendFileprivate sourcelib.sandbox.src.scanappendLinkscancapture
Static calls · unresolved targets: 1 · external targets: 4.
Called byCallsprivate sourcelib.sandbox.src.bwrapdiffUpperbwrapexecutetest sourcelib.sandbox.src.bwraptest: bubblewrap timeout removes a re...SnapshotappendSnapshotdeinitSnapshotinitSnapshotsortprivate sourcelib.sandbox.src.scanappendLinkscancapturePaths
Static calls · unresolved targets: 1 · external targets: 4.
Called byCallstest sourcelib.sandbox.src.scantest: scan diff records puts deletes ...ChangeSetappendChangeSetdeinitChangeSetinitchangesameEntryscandiff
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsNo direct callersprivate sourcelib.sandbox.src.scandigestAllocscanhashAlloc
Static calls · unresolved targets: 0 · external targets: 0.

Source: lib/sandbox/src/root.zig:43

zig
pub const scan = @import("scan.zig");

Source: lib/sandbox/src/scan.zig

zig
const std = @import("std");const sys = @import("sys");const change = @import("change.zig");const Allocator = std.mem.Allocator;const fs_io = sys.fs.debugIo();pub const Snapshot = struct {    allocator: Allocator,    entries: std.ArrayList(change.Entry) = .empty,    pub fn init(allocator: Allocator) Snapshot {        return .{ .allocator = allocator };    }    pub fn deinit(self: *Snapshot) void {        for (self.entries.items) |*entry| entry.deinit(self.allocator);        self.entries.deinit(self.allocator);        self.* = undefined;    }    pub fn append(self: *Snapshot, entry: change.Entry) Allocator.Error!void {        try self.entries.append(self.allocator, try entry.clone(self.allocator));    }    pub fn find(self: *const Snapshot, path: []const u8) ?change.Entry {        for (self.entries.items) |entry| {            if (std.mem.eql(u8, entry.path, path)) return entry;        }        return null;    }    pub fn sort(self: *Snapshot) void {        std.mem.sort(change.Entry, self.entries.items, {}, lessThan);    }};pub fn capture(root: sys.fs.Dir, allocator: Allocator) !Snapshot {    var snapshot = Snapshot.init(allocator);    errdefer snapshot.deinit();    var dir = try root.openDir(fs_io, ".", .{ .iterate = true });    defer dir.close(fs_io);    var walker = try dir.walk(allocator);    defer walker.deinit();    while (try walker.next(fs_io)) |entry| {        switch (entry.kind) {            .directory => try snapshot.append(.{                .path = @constCast(entry.path),                .kind = .directory,            }),            .file => try appendFile(root, allocator, &snapshot, entry.path),            .sym_link => try appendLink(root, &snapshot, entry.path),            else => try snapshot.append(.{                .path = @constCast(entry.path),                .kind = .other,            }),        }    }    snapshot.sort();    return snapshot;}pub fn capturePaths(root: sys.fs.Dir, allocator: Allocator) !Snapshot {    var snapshot = Snapshot.init(allocator);    errdefer snapshot.deinit();    var dir = try root.openDir(fs_io, ".", .{ .iterate = true });    defer dir.close(fs_io);    var walker = try dir.walk(allocator);    defer walker.deinit();    while (try walker.next(fs_io)) |entry| {        switch (entry.kind) {            .sym_link => try appendLink(root, &snapshot, entry.path),            .directory => try snapshot.append(.{ .path = @constCast(entry.path), .kind = .directory }),            .file => try snapshot.append(.{ .path = @constCast(entry.path), .kind = .file }),            else => try snapshot.append(.{ .path = @constCast(entry.path), .kind = .other }),        }    }    snapshot.sort();    return snapshot;}pub fn diff(allocator: Allocator, before: *const Snapshot, after: *const Snapshot) Allocator.Error!change.Set {    var set = change.Set.init(allocator);    errdefer set.deinit();    var before_index: usize = 0;    var after_index: usize = 0;    while (before_index < before.entries.items.len or after_index < after.entries.items.len) {        if (before_index >= before.entries.items.len) {            try set.append(.put, after.entries.items[after_index]);            after_index += 1;            continue;        }        if (after_index >= after.entries.items.len) {            try set.append(.delete, before.entries.items[before_index]);            before_index += 1;            continue;        }        const before_entry = before.entries.items[before_index];        const after_entry = after.entries.items[after_index];        const order = std.mem.order(u8, before_entry.path, after_entry.path);        switch (order) {            .lt => {                try set.append(.delete, before_entry);                before_index += 1;            },            .gt => {                try set.append(.put, after_entry);                after_index += 1;            },            .eq => {                if (!change.sameEntry(before_entry, after_entry)) try set.append(.put, after_entry);                before_index += 1;                after_index += 1;            },        }    }    return set;}pub fn hashAlloc(allocator: Allocator, content: []const u8) Allocator.Error![]u8 {    var digest: [std.crypto.hash.sha2.Sha256.digest_length]u8 = undefined;    std.crypto.hash.sha2.Sha256.hash(content, &digest, .{});    return try digestAlloc(allocator, &digest);}const FileDigest = struct {    hash: []u8,    bytes: u64,};fn digestAlloc(allocator: Allocator, digest: *const [std.crypto.hash.sha2.Sha256.digest_length]u8) Allocator.Error![]u8 {    const prefix = "sha256:";    const hex = std.fmt.bytesToHex(digest, .lower);    const value = try allocator.alloc(u8, prefix.len + hex.len);    @memcpy(value[0..prefix.len], prefix);    @memcpy(value[prefix.len..], hex[0..]);    return value;}fn appendFile(root: sys.fs.Dir, allocator: Allocator, snapshot: *Snapshot, path: []const u8) !void {    const digest = try hashFileAlloc(root, allocator, path);    defer allocator.free(digest.hash);    try snapshot.append(.{        .path = @constCast(path),        .kind = .file,        .hash = digest.hash,        .bytes = digest.bytes,    });}fn hashFileAlloc(root: sys.fs.Dir, allocator: Allocator, path: []const u8) !FileDigest {    var file = try root.openFile(fs_io, path, .{});    defer file.close(fs_io);    var reader_buffer: [16 * 1024]u8 = undefined;    var reader = file.reader(fs_io, &reader_buffer);    var chunk: [16 * 1024]u8 = undefined;    var hasher = std.crypto.hash.sha2.Sha256.init(.{});    var total: u64 = 0;    while (true) {        const count = reader.interface.readSliceShort(&chunk) catch |err| switch (err) {            error.ReadFailed => return reader.err orelse error.ReadFailed,        };        if (count == 0) break;        hasher.update(chunk[0..count]);        total += @intCast(count);    }    var digest: [std.crypto.hash.sha2.Sha256.digest_length]u8 = undefined;    hasher.final(&digest);    return .{        .hash = try digestAlloc(allocator, &digest),        .bytes = total,    };}fn appendLink(root: sys.fs.Dir, snapshot: *Snapshot, path: []const u8) !void {    var target_buffer: [std.fs.max_path_bytes]u8 = undefined;    const target_len = try root.readLink(fs_io, path, &target_buffer);    try snapshot.append(.{        .path = @constCast(path),        .kind = .sym_link,        .target = target_buffer[0..target_len],    });}fn lessThan(_: void, left: change.Entry, right: change.Entry) bool {    return std.mem.lessThan(u8, left.path, right.path);}test "scan captures files directories and links" {    var tmp = std.testing.tmpDir(.{});    defer tmp.cleanup();    try tmp.dir.createDirPath(fs_io, "dir");    try tmp.dir.writeFile(fs_io, .{ .sub_path = "dir/a.txt", .data = "alpha" });    tmp.dir.symLink(fs_io, "dir/a.txt", "link", .{}) catch |err| switch (err) {        error.AccessDenied, error.PermissionDenied => return error.SkipZigTest,        else => return err,    };    var snapshot = try capture(tmp.dir, std.testing.allocator);    defer snapshot.deinit();    try std.testing.expect(snapshot.find("dir") != null);    try std.testing.expectEqual(change.Kind.file, snapshot.find("dir/a.txt").?.kind);    try std.testing.expectEqual(change.Kind.sym_link, snapshot.find("link").?.kind);    try std.testing.expectEqualStrings("dir/a.txt", snapshot.find("link").?.target);}test "scan diff records puts deletes and content changes" {    var before = Snapshot.init(std.testing.allocator);    defer before.deinit();    var after = Snapshot.init(std.testing.allocator);    defer after.deinit();    try before.append(.{ .path = @constCast("gone.txt"), .kind = .file, .hash = @constCast("sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), .bytes = 1 });    try before.append(.{ .path = @constCast("same.txt"), .kind = .file, .hash = @constCast("sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"), .bytes = 1 });    try before.append(.{ .path = @constCast("update.txt"), .kind = .file, .hash = @constCast("sha256:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"), .bytes = 1 });    try after.append(.{ .path = @constCast("new.txt"), .kind = .file, .hash = @constCast("sha256:dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd"), .bytes = 1 });    try after.append(.{ .path = @constCast("same.txt"), .kind = .file, .hash = @constCast("sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"), .bytes = 1 });    try after.append(.{ .path = @constCast("update.txt"), .kind = .file, .hash = @constCast("sha256:eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"), .bytes = 2 });    before.sort();    after.sort();    var changes = try diff(std.testing.allocator, &before, &after);    defer changes.deinit();    try std.testing.expectEqual(@as(usize, 3), changes.len());    try std.testing.expectEqual(change.Operation.delete, changes.find("gone.txt").?.operation);    try std.testing.expectEqual(change.Operation.put, changes.find("new.txt").?.operation);    try std.testing.expectEqual(@as(u64, 2), changes.find("update.txt").?.entry.bytes);}

Audit

Definitions5
Public names5
Members0
Version26.7.0
Revisiondaab053ee433