tiny.smg.storage.source
Defined in storage.
API (7)
Actions
Public operations.
executableDigestmatchesownerForHead: Reads the checksummed scanner and project identity bound to one stored graph head.projectForOwnerwriteForHead
Types and contracts
Public types and contracts.
Source
Source: tools/smg/src/storage/root.zig:11
zig
pub const source = @import("source.zig");Source: tools/smg/src/storage/source.zig
zig
const std = @import("std");const sql = @import("sql");const sys = @import("sys");const smg = @import("../root.zig");const storage = smg.storage;const fs_io = std.Options.debug_io;pub const Digest = [std.crypto.hash.sha2.Sha256.digest_length]u8;pub const Owner = struct { executable: Digest, project: Digest };const magic = "tiny.smg.source/v1\n";const identity_bytes = @sizeOf(sql.Hash) + 2 * @sizeOf(Digest);const payload_bytes = magic.len + identity_bytes;const file_bytes = payload_bytes + @sizeOf(Digest);const read_buffer_bytes = 64 * 1024;pub fn executableDigest(allocator: std.mem.Allocator) !Digest { const executable = try std.process.executablePathAlloc(fs_io, allocator); defer allocator.free(executable); var file = try std.Io.Dir.openFileAbsolute(fs_io, executable, .{}); defer file.close(fs_io); var reader_buffer: [read_buffer_bytes]u8 = undefined; var file_reader = file.reader(fs_io, &reader_buffer); var chunk: [read_buffer_bytes]u8 = undefined; var hasher = std.crypto.hash.sha2.Sha256.init(.{}); while (true) { const count = file_reader.interface.readSliceShort(&chunk) catch |err| switch (err) { error.ReadFailed => return file_reader.err orelse error.ReadFailed, }; if (count == 0) break; hasher.update(chunk[0..count]); } var digest: Digest = undefined; hasher.final(&digest); return digest;}pub fn matches( allocator: std.mem.Allocator, root: []const u8, expected_head: sql.Hash, executable: Digest, project: Digest,) std.mem.Allocator.Error!bool { const stored = try projectForOwner(allocator, root, expected_head, executable) orelse return false; return std.mem.eql(u8, &stored, &project);}pub fn projectForOwner( allocator: std.mem.Allocator, root: []const u8, expected_head: sql.Hash, executable: Digest,) std.mem.Allocator.Error!?Digest { const owner = try ownerForHead(allocator, root, expected_head) orelse return null; if (!std.mem.eql(u8, &owner.executable, &executable)) return null; return owner.project;}/// Reads the checksummed scanner and project identity bound to one stored graph head.pub fn ownerForHead( allocator: std.mem.Allocator, root: []const u8, expected_head: sql.Hash,) std.mem.Allocator.Error!?Owner { const path = try cachePath(allocator, root, storage.paths.source_file_name); defer allocator.free(path); var file = std.Io.Dir.openFileAbsolute(fs_io, path, .{}) catch return null; defer file.close(fs_io); const stat = file.stat(fs_io) catch return null; if (stat.kind != .file or stat.size != file_bytes) return null; var encoded: [file_bytes]u8 = undefined; const read = file.readPositionalAll(fs_io, &encoded, 0) catch return null; if (read != encoded.len) return null; if (!std.mem.eql(u8, encoded[0..magic.len], magic)) return null; var computed: Digest = undefined; std.crypto.hash.sha2.Sha256.hash(encoded[0..payload_bytes], &computed, .{}); if (!std.mem.eql(u8, &computed, encoded[payload_bytes..])) return null; var offset: usize = magic.len; if (!std.mem.eql(u8, expected_head[0..], encoded[offset .. offset + @sizeOf(sql.Hash)])) return null; offset += @sizeOf(sql.Hash); var executable: Digest = undefined; @memcpy(&executable, encoded[offset .. offset + @sizeOf(Digest)]); offset += @sizeOf(Digest); var project: Digest = undefined; @memcpy(&project, encoded[offset .. offset + @sizeOf(Digest)]); return .{ .executable = executable, .project = project };}pub fn writeForHead( allocator: std.mem.Allocator, root: []const u8, head: sql.Hash, executable: Digest, project: Digest,) !void { const temporary_name = try std.fmt.allocPrint( allocator, "{s}.{d}", .{ storage.paths.source_temp_file_name, try sys.process.currentProcessId() }, ); defer allocator.free(temporary_name); const temporary = try cachePath(allocator, root, temporary_name); defer allocator.free(temporary); const destination = try cachePath(allocator, root, storage.paths.source_file_name); defer allocator.free(destination); var encoded: [file_bytes]u8 = undefined; var offset: usize = 0; @memcpy(encoded[offset .. offset + magic.len], magic); offset += magic.len; @memcpy(encoded[offset .. offset + @sizeOf(sql.Hash)], &head); offset += @sizeOf(sql.Hash); @memcpy(encoded[offset .. offset + @sizeOf(Digest)], &executable); offset += @sizeOf(Digest); @memcpy(encoded[offset .. offset + @sizeOf(Digest)], &project); offset += @sizeOf(Digest); std.debug.assert(offset == payload_bytes); var digest: Digest = undefined; std.crypto.hash.sha2.Sha256.hash(encoded[0..payload_bytes], &digest, .{}); @memcpy(encoded[payload_bytes..], &digest); errdefer std.Io.Dir.deleteFileAbsolute(fs_io, temporary) catch {}; { var file = try std.Io.Dir.createFileAbsolute(fs_io, temporary, .{ .truncate = true }); defer file.close(fs_io); try file.writeStreamingAll(fs_io, &encoded); try file.sync(fs_io); } try sys.fs.rename(temporary, destination);}fn cachePath( allocator: std.mem.Allocator, root: []const u8, name: []const u8,) std.mem.Allocator.Error![]const u8 { return try std.fs.path.join( allocator, &.{ root, storage.paths.smg_dir_name, name }, );}test "source identity cache matches only exact graph executable and project" { var arena = std.heap.ArenaAllocator.init(std.testing.allocator); defer arena.deinit(); const allocator = arena.allocator(); const root = try std.fmt.allocPrint(allocator, "/tmp/smg-source-identity-test-{x}", .{@intFromPtr(&arena)}); defer sys.fs.deleteTree(root) catch {}; try sys.fs.createDirPath(try std.fs.path.join(allocator, &.{ root, storage.paths.smg_dir_name })); const head: sql.Hash = @splat(1); const executable: Digest = @splat(2); const project: Digest = @splat(3); try writeForHead(allocator, root, head, executable, project); const owner = (try ownerForHead(allocator, root, head)).?; try std.testing.expectEqualSlices(u8, &executable, &owner.executable); try std.testing.expectEqualSlices(u8, &project, &owner.project); try std.testing.expect(try ownerForHead(allocator, root, @splat(4)) == null); try std.testing.expect(try matches(allocator, root, head, executable, project)); try std.testing.expect(!try matches(allocator, root, @as(sql.Hash, @splat(4)), executable, project)); try std.testing.expect(!try matches(allocator, root, head, @as(Digest, @splat(4)), project)); try std.testing.expect(!try matches(allocator, root, head, executable, @as(Digest, @splat(4)))); const path = try cachePath(allocator, root, storage.paths.source_file_name); var file = try std.Io.Dir.openFileAbsolute(fs_io, path, .{ .mode = .read_write }); defer file.close(fs_io); try file.writePositionalAll(fs_io, "x", magic.len + 1); try std.testing.expect(!try matches(allocator, root, head, executable, project));}Audit
| Definitions | 8 |
|---|---|
| Public names | 8 |
| Members | 2 |
| Version | 26.7.0 |
| Revision | daab053ee433 |