const std = @import("std");const io_mod = @import("../shared/io.zig");const profile_paths = @import("../shared/profile_paths.zig");const generation_fact_codec = @import("generation_fact_codec.zig");const usage_report = @import("usage_report.zig"); const Allocator = std.mem.Allocator;const usage_file = profile_paths.usage_file_name;const usage_lock_file = "usage.lock";const lock_deadline_ms: u64 = 2000;const max_file_bytes: usize = 32 * 1024 * 1024;const max_record_bytes: usize = 16 * 1024;const max_records: usize = 200_000;const compaction_threshold_bytes: u64 = 8 * 1024 * 1024;const retention_ms: i64 = std.time.ms_per_day * 35;const private_dir_permissions = std.Io.Dir.Permissions.fromMode(0o700);const private_file_permissions = std.Io.File.Permissions.fromMode(0o600); pub const AppendOutcome = enum { appended, duplicate, conflict,}; pub const Loaded = struct { coverage_started_at_ms: ?i64, facts: []usage_report.GenerationFact, pending: []usage_report.PendingMarker, incidents: []usage_report.Incident, record_count: usize, pub fn deinit(self: *Loaded, alloc: Allocator) void { for (self.facts) |*fact| fact.deinit(alloc); alloc.free(self.facts); for (self.pending) |*marker| marker.deinit(alloc); alloc.free(self.pending); alloc.free(self.incidents); self.* = undefined; }}; const ParsedRecord = union(enum) { coverage: i64, generation: usage_report.GenerationFact, pending: usage_report.PendingMarker, incident: usage_report.Incident, fn deinit(self: *ParsedRecord, alloc: Allocator) void { switch (self.*) { .generation => |*fact| fact.deinit(alloc), .pending => |*marker| marker.deinit(alloc), .coverage, .incident => {}, } self.* = undefined; }}; const AppendDecision = struct { outcome: AppendOutcome, write: bool,}; const VariantIndexes = struct { first: usize, second: ?usize = null,}; const TailBoundary = struct { length: u64, incomplete: bool,}; pub const Store = struct { home_path: []u8, durable_home: ?io_mod.VerifiedDir = null, lock_ops: io_mod.LockOps = .{}, pub fn initFromHome(alloc: Allocator, home_path: []const u8) !Store { const owned_home = try alloc.dupe(u8, home_path); errdefer alloc.free(owned_home); return .{ .home_path = owned_home, .durable_home = try openExistingDurableHome(home_path), }; } pub fn deinit(self: *Store, alloc: Allocator) void { if (self.durable_home) |*dir| dir.close(); alloc.free(self.home_path); self.* = undefined; } fn appendFact( self: *Store, alloc: Allocator, fact: usage_report.GenerationFact, ) !AppendOutcome { return self.appendEvent(alloc, .{ .generation = fact }); } pub fn appendEvent( self: *Store, alloc: Allocator, event: usage_report.ProfileEvent, ) !AppendOutcome { try validateEvent(event); const event_line = try serializeEvent(alloc, event); defer alloc.free(event_line); if (event_line.len > max_record_bytes) return error.UsageRecordTooLarge; try self.ensureWritable(); var lock = self.acquireLock() catch |err| switch (err) { error.LockBusy => return error.UsageLockBusy, error.LockUnsupported => return error.UsageLockUnsupported, else => return err, }; defer lock.release(); var file: ?std.Io.File = (try self.openUsage(true, true)).?; defer if (file) |open_file| open_file.close(io_mod.getIo()); const tail = try inspectTail(file.?); const has_incomplete_tail = tail.incomplete; var boundary = tail.length; var loaded = try self.loadFromFile(alloc, file.?, boundary); defer loaded.deinit(alloc); const decision = classifyEvent(loaded, event); const now_ms = @max(io_mod.milliTimestamp(), 0); var append_bytes: std.Io.Writer.Allocating = .init(alloc); defer append_bytes.deinit(); var append_record_count: usize = 0; if (loaded.coverage_started_at_ms == null) { try writeCoverage(&append_bytes.writer, now_ms); append_record_count += 1; } if (has_incomplete_tail) { try writeIncident(&append_bytes.writer, .{ .occurred_at_ms = now_ms, .completeness = .incomplete, }); append_record_count += 1; } if (decision.write) { try append_bytes.writer.writeAll(event_line); append_record_count += 1; } if (append_bytes.written().len == 0) return decision.outcome; var next_length = std.math.add( u64, boundary, std.math.cast(u64, append_bytes.written().len) orelse return error.UsageCapacityExceeded, ) catch return error.UsageCapacityExceeded; var compacted_before_append = false; var append_committed = false; var base_record_count = loaded.record_count; if (shouldCompactBeforeAppend( next_length, base_record_count, append_record_count, loaded, now_ms, )) { compacted_before_append = true; base_record_count = retainedRecordCount(loaded, now_ms); if (has_incomplete_tail) { var replacement: std.Io.Writer.Allocating = .init(alloc); defer replacement.deinit(); try writeRetainedRecords(&replacement.writer, loaded, now_ms); try replacement.writer.writeAll(append_bytes.written()); next_length = std.math.cast(u64, replacement.written().len) orelse return error.UsageCapacityExceeded; if (next_length > max_file_bytes) return error.UsageCapacityExceeded; _ = try checkedRecordCount(base_record_count, append_record_count); try self.replaceUsageLocked(alloc, replacement.written()); file.?.close(io_mod.getIo()); file = null; append_committed = true; } else { file.?.close(io_mod.getIo()); file = null; try self.compactLocked(alloc, now_ms); file = (try self.openUsage(true, false)) orelse return error.UsageReadFailed; boundary = try file.?.length(io_mod.getIo()); next_length = std.math.add( u64, boundary, std.math.cast(u64, append_bytes.written().len) orelse return error.UsageCapacityExceeded, ) catch return error.UsageCapacityExceeded; } } if (next_length > max_file_bytes) return error.UsageCapacityExceeded; _ = try checkedRecordCount(base_record_count, append_record_count); if (!append_committed) { if (has_incomplete_tail) { var replacement: std.Io.Writer.Allocating = .init(alloc); defer replacement.deinit(); try copyFilePrefix( &replacement.writer, file.?, boundary, ); try replacement.writer.writeAll(append_bytes.written()); try self.replaceUsageLocked(alloc, replacement.written()); file.?.close(io_mod.getIo()); file = null; } else { try file.?.writePositionalAll(io_mod.getIo(), append_bytes.written(), boundary); file.?.sync(io_mod.getIo()) catch return error.UsageWriteFailed; } } if (shouldCompactAfterAppend( next_length, loaded, eventTimestamp(event), now_ms, compacted_before_append, )) { if (file) |open_file| { open_file.close(io_mod.getIo()); file = null; } try self.compactLocked(alloc, now_ms); } return decision.outcome; } /// Loads one stable read-only boundary without creating profile state. pub fn load(self: *Store, alloc: Allocator) !Loaded { if (self.durable_home == null) { self.durable_home = try openExistingDurableHome(self.home_path); } try self.validateReadable(); while (true) { const existing_lock = self.acquireExistingLock() catch |err| switch (err) { error.LockBusy => return error.UsageLockBusy, error.LockUnsupported => return error.UsageLockUnsupported, else => return err, }; if (existing_lock) |held| { var lock = held; defer lock.release(); return self.loadUnlocked(alloc); } var loaded = try self.loadUnlocked(alloc); if (!try self.lockFileExists()) return loaded; loaded.deinit(alloc); } } fn loadUnlocked(self: *Store, alloc: Allocator) !Loaded { var file = (try self.openUsage(false, false)) orelse return .{ .coverage_started_at_ms = null, .facts = try alloc.alloc(usage_report.GenerationFact, 0), .pending = try alloc.alloc(usage_report.PendingMarker, 0), .incidents = try alloc.alloc(usage_report.Incident, 0), .record_count = 0, }; defer file.close(io_mod.getIo()); const boundary = try file.length(io_mod.getIo()); return self.loadFromFile(alloc, file, boundary); } fn validateReadable(self: *Store) !void { const durable_home = self.durable_home orelse return; const stat = try durable_home.dir.stat(io_mod.getIo()); if (stat.kind != .directory) return error.DurablePathUnsafe; if (stat.permissions.toMode() & 0o777 != 0o700) { return error.PrivateStatePermissionsUnsupported; } } fn ensureWritable(self: *Store) !void { if (self.durable_home == null) { const zio = io_mod.getIo(); var home = io_mod.VerifiedDir{ .dir = std.Io.Dir.openDirAbsolute(zio, self.home_path, .{ .iterate = true, }) catch return error.DurableLayoutFailed, }; defer home.close(); self.durable_home = io_mod.openOrCreateVerifiedPrivateDir( &home, profile_paths.root_dir_name, ) catch |err| switch (err) { error.PrivateStatePermissionsUnsupported, error.DurablePathUnsafe => return err, else => return error.DurableLayoutFailed, }; } self.durable_home.?.dir.setPermissions( io_mod.getIo(), private_dir_permissions, ) catch return error.PrivateStatePermissionsUnsupported; const stat = try self.durable_home.?.dir.stat(io_mod.getIo()); if (stat.kind != .directory) return error.DurablePathUnsafe; if (stat.permissions.toMode() & 0o777 != 0o700) { return error.PrivateStatePermissionsUnsupported; } } fn acquireLock(self: *Store) !io_mod.TimedAdvisoryLock { return io_mod.acquireTimedAdvisoryLockWithOps( &self.durable_home.?, usage_lock_file, lock_deadline_ms, self.lock_ops, ); } fn acquireExistingLock(self: *Store) !?io_mod.TimedAdvisoryLock { const durable_home = self.durable_home orelse return null; const zio = io_mod.getIo(); const file = durable_home.dir.openFile(zio, usage_lock_file, .{ .mode = .read_write, .allow_directory = false, .follow_symlinks = false, .resolve_beneath = true, }) catch |err| switch (err) { error.FileNotFound => return null, error.SymLinkLoop, error.IsDir, error.NotDir => return error.DurablePathUnsafe, else => return err, }; errdefer file.close(zio); const stat = try file.stat(zio); if (stat.kind != .file or stat.nlink != 1) { return error.DurablePathUnsafe; } if (stat.permissions.toMode() & 0o777 != 0o600) { return error.PrivateStatePermissionsUnsupported; } const started = self.lock_ops.now_ms(self.lock_ops.ctx); const deadline = started + @as(i64, @intCast(lock_deadline_ms)); while (true) { const locked = self.lock_ops.try_lock( self.lock_ops.ctx, file, ) catch |err| switch (err) { error.FileLocksUnsupported => return error.LockUnsupported, else => return err, }; if (locked) return .{ .file = file }; if (self.lock_ops.now_ms(self.lock_ops.ctx) >= deadline) { return error.LockBusy; } self.lock_ops.sleep_ms( self.lock_ops.ctx, @min(@as(u64, 10), lock_deadline_ms), ); } } fn lockFileExists(self: *Store) !bool { const durable_home = self.durable_home orelse return false; const stat = durable_home.dir.statFile(io_mod.getIo(), usage_lock_file, .{ .follow_symlinks = false, }) catch |err| switch (err) { error.FileNotFound => return false, error.SymLinkLoop, error.NotDir => return error.DurablePathUnsafe, else => return err, }; if (stat.kind != .file or stat.nlink != 1) { return error.DurablePathUnsafe; } if (stat.permissions.toMode() & 0o777 != 0o600) { return error.PrivateStatePermissionsUnsupported; } return true; } fn openUsage( self: *Store, writable: bool, create: bool, ) !?std.Io.File { if (self.durable_home == null) return null; const zio = io_mod.getIo(); var created = false; var file = openExistingUsageFile( self.durable_home.?.dir, if (writable) .read_write else .read_only, ) catch |err| switch (err) { error.FileNotFound => create_file: { if (!create) return null; const new_file = self.durable_home.?.dir.createFile(zio, usage_file, .{ .read = true, .truncate = false, .exclusive = true, .permissions = private_file_permissions, .resolve_beneath = true, }) catch |create_err| switch (create_err) { error.PathAlreadyExists => return self.openUsage(writable, false), else => return create_err, }; created = true; break :create_file new_file; }, error.SymLinkLoop, error.IsDir, error.NotDir => return error.DurablePathUnsafe, else => return err, }; errdefer file.close(zio); const initial = try file.stat(zio); try io_mod.verifyOpenedRegularFile( initial, if (writable) .read_write else .read_only, ); if (writable) { file.setPermissions(zio, private_file_permissions) catch return error.PrivateStatePermissionsUnsupported; } const verified = if (writable) try file.stat(zio) else initial; if (verified.permissions.toMode() & 0o777 != 0o600) { return error.PrivateStatePermissionsUnsupported; } if (created) { io_mod.syncVerifiedDir(self.durable_home.?.dir) catch return error.DurableLayoutFailed; } return file; } fn loadFromFile( self: *Store, alloc: Allocator, file: std.Io.File, boundary: u64, ) !Loaded { _ = self; if (boundary > max_file_bytes) return error.UsageCapacityExceeded; if (boundary == 0) { return .{ .coverage_started_at_ms = null, .facts = try alloc.alloc(usage_report.GenerationFact, 0), .pending = try alloc.alloc(usage_report.PendingMarker, 0), .incidents = try alloc.alloc(usage_report.Incident, 0), .record_count = 0, }; } const byte_len = std.math.cast(usize, boundary) orelse return error.UsageCapacityExceeded; const bytes = try alloc.alloc(u8, byte_len); defer alloc.free(bytes); const read_count = try file.readPositionalAll(io_mod.getIo(), bytes, 0); if (read_count != byte_len) return error.UsageReadFailed; if (bytes[bytes.len - 1] != '\n') return error.UsageStoreIncomplete; var facts: std.ArrayList(usage_report.GenerationFact) = .empty; errdefer { for (facts.items) |*fact| fact.deinit(alloc); facts.deinit(alloc); } var pending: std.ArrayList(usage_report.PendingMarker) = .empty; errdefer { for (pending.items) |*marker| marker.deinit(alloc); pending.deinit(alloc); } var incidents: std.ArrayList(usage_report.Incident) = .empty; errdefer incidents.deinit(alloc); var fact_indexes: std.StringHashMapUnmanaged(VariantIndexes) = .empty; defer fact_indexes.deinit(alloc); var pending_indexes: std.StringHashMapUnmanaged(VariantIndexes) = .empty; defer pending_indexes.deinit(alloc); var coverage_started_at_ms: ?i64 = null; var record_count: usize = 0; var lines = std.mem.splitScalar(u8, bytes, '\n'); while (lines.next()) |line| { if (line.len == 0) continue; record_count += 1; if (record_count > max_records or line.len > max_record_bytes) { return error.UsageCapacityExceeded; } var record = try parseRecord(alloc, line); defer record.deinit(alloc); switch (record) { .coverage => |started_at_ms| { if (coverage_started_at_ms) |existing| { if (existing != started_at_ms) return error.InvalidUsageStore; } else { coverage_started_at_ms = started_at_ms; } }, .generation => |fact| { if (coverage_started_at_ms == null) return error.InvalidUsageStore; if (fact_indexes.getPtr(fact.id)) |indexes| { if (usage_report.GenerationFact.eql( facts.items[indexes.first], fact, ) or (indexes.second != null and usage_report.GenerationFact.eql( facts.items[indexes.second.?], fact, ))) {Lines 1–500 of 1676. Load more
const std = @import("std"); const io_mod = @import("../shared/io.zig"); const profile_paths = @import("../shared/profile_paths.zig"); const generation_fact_codec = @import("generation_fact_codec.zig"); const usage_report = @import("usage_report.zig"); const Allocator = std.mem.Allocator; const usage_file = profile_paths.usage_file_name; const usage_lock_file = "usage.lock"; const lock_deadline_ms: u64 = 2000; const max_file_bytes: usize = 32 * 1024 * 1024; const max_record_bytes: usize = 16 * 1024; const max_records: usize = 200_000; const compaction_threshold_bytes: u64 = 8 * 1024 * 1024; const retention_ms: i64 = std.time.ms_per_day * 35; const private_dir_permissions = std.Io.Dir.Permissions.fromMode(0o700); const private_file_permissions = std.Io.File.Permissions.fromMode(0o600); pub const AppendOutcome = enum { appended, duplicate, conflict, }; pub const Loaded = struct { coverage_started_at_ms: ?i64, facts: []usage_report.GenerationFact, pending: []usage_report.PendingMarker, incidents: []usage_report.Incident, record_count: usize, pub fn deinit(self: *Loaded, alloc: Allocator) void { for (self.facts) |*fact| fact.deinit(alloc); alloc.free(self.facts); for (self.pending) |*marker| marker.deinit(alloc); alloc.free(self.pending); alloc.free(self.incidents); self.* = undefined; } }; const ParsedRecord = union(enum) { coverage: i64, generation: usage_report.GenerationFact, pending: usage_report.PendingMarker, incident: usage_report.Incident, fn deinit(self: *ParsedRecord, alloc: Allocator) void { switch (self.*) { .generation => |*fact| fact.deinit(alloc), .pending => |*marker| marker.deinit(alloc), .coverage, .incident => {}, } self.* = undefined; } }; const AppendDecision = struct { outcome: AppendOutcome, write: bool, }; const VariantIndexes = struct { first: usize, second: ?usize = null, }; const TailBoundary = struct { length: u64, incomplete: bool, }; pub const Store = struct { home_path: []u8, durable_home: ?io_mod.VerifiedDir = null, lock_ops: io_mod.LockOps = .{}, pub fn initFromHome(alloc: Allocator, home_path: []const u8) !Store { const owned_home = try alloc.dupe(u8, home_path); errdefer alloc.free(owned_home); return .{ .home_path = owned_home, .durable_home = try openExistingDurableHome(home_path), }; } pub fn deinit(self: *Store, alloc: Allocator) void { if (self.durable_home) |*dir| dir.close(); alloc.free(self.home_path); self.* = undefined; } fn appendFact( self: *Store, alloc: Allocator, fact: usage_report.GenerationFact, ) !AppendOutcome { return self.appendEvent(alloc, .{ .generation = fact }); } pub fn appendEvent( self: *Store, alloc: Allocator, event: usage_report.ProfileEvent, ) !AppendOutcome { try validateEvent(event); const event_line = try serializeEvent(alloc, event); defer alloc.free(event_line); if (event_line.len > max_record_bytes) return error.UsageRecordTooLarge; try self.ensureWritable(); var lock = self.acquireLock() catch |err| switch (err) { error.LockBusy => return error.UsageLockBusy, error.LockUnsupported => return error.UsageLockUnsupported, else => return err, }; defer lock.release(); var file: ?std.Io.File = (try self.openUsage(true, true)).?; defer if (file) |open_file| open_file.close(io_mod.getIo()); const tail = try inspectTail(file.?); const has_incomplete_tail = tail.incomplete; var boundary = tail.length; var loaded = try self.loadFromFile(alloc, file.?, boundary); defer loaded.deinit(alloc); const decision = classifyEvent(loaded, event); const now_ms = @max(io_mod.milliTimestamp(), 0); var append_bytes: std.Io.Writer.Allocating = .init(alloc); defer append_bytes.deinit(); var append_record_count: usize = 0; if (loaded.coverage_started_at_ms == null) { try writeCoverage(&append_bytes.writer, now_ms); append_record_count += 1; } if (has_incomplete_tail) { try writeIncident(&append_bytes.writer, .{ .occurred_at_ms = now_ms, .completeness = .incomplete, }); append_record_count += 1; } if (decision.write) { try append_bytes.writer.writeAll(event_line); append_record_count += 1; } if (append_bytes.written().len == 0) return decision.outcome; var next_length = std.math.add( u64, boundary, std.math.cast(u64, append_bytes.written().len) orelse return error.UsageCapacityExceeded, ) catch return error.UsageCapacityExceeded; var compacted_before_append = false; var append_committed = false; var base_record_count = loaded.record_count; if (shouldCompactBeforeAppend( next_length, base_record_count, append_record_count, loaded, now_ms, )) { compacted_before_append = true; base_record_count = retainedRecordCount(loaded, now_ms); if (has_incomplete_tail) { var replacement: std.Io.Writer.Allocating = .init(alloc); defer replacement.deinit(); try writeRetainedRecords(&replacement.writer, loaded, now_ms); try replacement.writer.writeAll(append_bytes.written()); next_length = std.math.cast(u64, replacement.written().len) orelse return error.UsageCapacityExceeded; if (next_length > max_file_bytes) return error.UsageCapacityExceeded; _ = try checkedRecordCount(base_record_count, append_record_count); try self.replaceUsageLocked(alloc, replacement.written()); file.?.close(io_mod.getIo()); file = null; append_committed = true; } else { file.?.close(io_mod.getIo()); file = null; try self.compactLocked(alloc, now_ms); file = (try self.openUsage(true, false)) orelse return error.UsageReadFailed; boundary = try file.?.length(io_mod.getIo()); next_length = std.math.add( u64, boundary, std.math.cast(u64, append_bytes.written().len) orelse return error.UsageCapacityExceeded, ) catch return error.UsageCapacityExceeded; } } if (next_length > max_file_bytes) return error.UsageCapacityExceeded; _ = try checkedRecordCount(base_record_count, append_record_count); if (!append_committed) { if (has_incomplete_tail) { var replacement: std.Io.Writer.Allocating = .init(alloc); defer replacement.deinit(); try copyFilePrefix( &replacement.writer, file.?, boundary, ); try replacement.writer.writeAll(append_bytes.written()); try self.replaceUsageLocked(alloc, replacement.written()); file.?.close(io_mod.getIo()); file = null; } else { try file.?.writePositionalAll(io_mod.getIo(), append_bytes.written(), boundary); file.?.sync(io_mod.getIo()) catch return error.UsageWriteFailed; } } if (shouldCompactAfterAppend( next_length, loaded, eventTimestamp(event), now_ms, compacted_before_append, )) { if (file) |open_file| { open_file.close(io_mod.getIo()); file = null; } try self.compactLocked(alloc, now_ms); } return decision.outcome; } /// Loads one stable read-only boundary without creating profile state. pub fn load(self: *Store, alloc: Allocator) !Loaded { if (self.durable_home == null) { self.durable_home = try openExistingDurableHome(self.home_path); } try self.validateReadable(); while (true) { const existing_lock = self.acquireExistingLock() catch |err| switch (err) { error.LockBusy => return error.UsageLockBusy, error.LockUnsupported => return error.UsageLockUnsupported, else => return err, }; if (existing_lock) |held| { var lock = held; defer lock.release(); return self.loadUnlocked(alloc); } var loaded = try self.loadUnlocked(alloc); if (!try self.lockFileExists()) return loaded; loaded.deinit(alloc); } } fn loadUnlocked(self: *Store, alloc: Allocator) !Loaded { var file = (try self.openUsage(false, false)) orelse return .{ .coverage_started_at_ms = null, .facts = try alloc.alloc(usage_report.GenerationFact, 0), .pending = try alloc.alloc(usage_report.PendingMarker, 0), .incidents = try alloc.alloc(usage_report.Incident, 0), .record_count = 0, }; defer file.close(io_mod.getIo()); const boundary = try file.length(io_mod.getIo()); return self.loadFromFile(alloc, file, boundary); } fn validateReadable(self: *Store) !void { const durable_home = self.durable_home orelse return; const stat = try durable_home.dir.stat(io_mod.getIo()); if (stat.kind != .directory) return error.DurablePathUnsafe; if (stat.permissions.toMode() & 0o777 != 0o700) { return error.PrivateStatePermissionsUnsupported; } } fn ensureWritable(self: *Store) !void { if (self.durable_home == null) { const zio = io_mod.getIo(); var home = io_mod.VerifiedDir{ .dir = std.Io.Dir.openDirAbsolute(zio, self.home_path, .{ .iterate = true, }) catch return error.DurableLayoutFailed, }; defer home.close(); self.durable_home = io_mod.openOrCreateVerifiedPrivateDir( &home, profile_paths.root_dir_name, ) catch |err| switch (err) { error.PrivateStatePermissionsUnsupported, error.DurablePathUnsafe => return err, else => return error.DurableLayoutFailed, }; } self.durable_home.?.dir.setPermissions( io_mod.getIo(), private_dir_permissions, ) catch return error.PrivateStatePermissionsUnsupported; const stat = try self.durable_home.?.dir.stat(io_mod.getIo()); if (stat.kind != .directory) return error.DurablePathUnsafe; if (stat.permissions.toMode() & 0o777 != 0o700) { return error.PrivateStatePermissionsUnsupported; } } fn acquireLock(self: *Store) !io_mod.TimedAdvisoryLock { return io_mod.acquireTimedAdvisoryLockWithOps( &self.durable_home.?, usage_lock_file, lock_deadline_ms, self.lock_ops, ); } fn acquireExistingLock(self: *Store) !?io_mod.TimedAdvisoryLock { const durable_home = self.durable_home orelse return null; const zio = io_mod.getIo(); const file = durable_home.dir.openFile(zio, usage_lock_file, .{ .mode = .read_write, .allow_directory = false, .follow_symlinks = false, .resolve_beneath = true, }) catch |err| switch (err) { error.FileNotFound => return null, error.SymLinkLoop, error.IsDir, error.NotDir => return error.DurablePathUnsafe, else => return err, }; errdefer file.close(zio); const stat = try file.stat(zio); if (stat.kind != .file or stat.nlink != 1) { return error.DurablePathUnsafe; } if (stat.permissions.toMode() & 0o777 != 0o600) { return error.PrivateStatePermissionsUnsupported; } const started = self.lock_ops.now_ms(self.lock_ops.ctx); const deadline = started + @as(i64, @intCast(lock_deadline_ms)); while (true) { const locked = self.lock_ops.try_lock( self.lock_ops.ctx, file, ) catch |err| switch (err) { error.FileLocksUnsupported => return error.LockUnsupported, else => return err, }; if (locked) return .{ .file = file }; if (self.lock_ops.now_ms(self.lock_ops.ctx) >= deadline) { return error.LockBusy; } self.lock_ops.sleep_ms( self.lock_ops.ctx, @min(@as(u64, 10), lock_deadline_ms), ); } } fn lockFileExists(self: *Store) !bool { const durable_home = self.durable_home orelse return false; const stat = durable_home.dir.statFile(io_mod.getIo(), usage_lock_file, .{ .follow_symlinks = false, }) catch |err| switch (err) { error.FileNotFound => return false, error.SymLinkLoop, error.NotDir => return error.DurablePathUnsafe, else => return err, }; if (stat.kind != .file or stat.nlink != 1) { return error.DurablePathUnsafe; } if (stat.permissions.toMode() & 0o777 != 0o600) { return error.PrivateStatePermissionsUnsupported; } return true; } fn openUsage( self: *Store, writable: bool, create: bool, ) !?std.Io.File { if (self.durable_home == null) return null; const zio = io_mod.getIo(); var created = false; var file = openExistingUsageFile( self.durable_home.?.dir, if (writable) .read_write else .read_only, ) catch |err| switch (err) { error.FileNotFound => create_file: { if (!create) return null; const new_file = self.durable_home.?.dir.createFile(zio, usage_file, .{ .read = true, .truncate = false, .exclusive = true, .permissions = private_file_permissions, .resolve_beneath = true, }) catch |create_err| switch (create_err) { error.PathAlreadyExists => return self.openUsage(writable, false), else => return create_err, }; created = true; break :create_file new_file; }, error.SymLinkLoop, error.IsDir, error.NotDir => return error.DurablePathUnsafe, else => return err, }; errdefer file.close(zio); const initial = try file.stat(zio); try io_mod.verifyOpenedRegularFile( initial, if (writable) .read_write else .read_only, ); if (writable) { file.setPermissions(zio, private_file_permissions) catch return error.PrivateStatePermissionsUnsupported; } const verified = if (writable) try file.stat(zio) else initial; if (verified.permissions.toMode() & 0o777 != 0o600) { return error.PrivateStatePermissionsUnsupported; } if (created) { io_mod.syncVerifiedDir(self.durable_home.?.dir) catch return error.DurableLayoutFailed; } return file; } fn loadFromFile( self: *Store, alloc: Allocator, file: std.Io.File, boundary: u64, ) !Loaded { _ = self; if (boundary > max_file_bytes) return error.UsageCapacityExceeded; if (boundary == 0) { return .{ .coverage_started_at_ms = null, .facts = try alloc.alloc(usage_report.GenerationFact, 0), .pending = try alloc.alloc(usage_report.PendingMarker, 0), .incidents = try alloc.alloc(usage_report.Incident, 0), .record_count = 0, }; } const byte_len = std.math.cast(usize, boundary) orelse return error.UsageCapacityExceeded; const bytes = try alloc.alloc(u8, byte_len); defer alloc.free(bytes); const read_count = try file.readPositionalAll(io_mod.getIo(), bytes, 0); if (read_count != byte_len) return error.UsageReadFailed; if (bytes[bytes.len - 1] != '\n') return error.UsageStoreIncomplete; var facts: std.ArrayList(usage_report.GenerationFact) = .empty; errdefer { for (facts.items) |*fact| fact.deinit(alloc); facts.deinit(alloc); } var pending: std.ArrayList(usage_report.PendingMarker) = .empty; errdefer { for (pending.items) |*marker| marker.deinit(alloc); pending.deinit(alloc); } var incidents: std.ArrayList(usage_report.Incident) = .empty; errdefer incidents.deinit(alloc); var fact_indexes: std.StringHashMapUnmanaged(VariantIndexes) = .empty; defer fact_indexes.deinit(alloc); var pending_indexes: std.StringHashMapUnmanaged(VariantIndexes) = .empty; defer pending_indexes.deinit(alloc); var coverage_started_at_ms: ?i64 = null; var record_count: usize = 0; var lines = std.mem.splitScalar(u8, bytes, '\n'); while (lines.next()) |line| { if (line.len == 0) continue; record_count += 1; if (record_count > max_records or line.len > max_record_bytes) { return error.UsageCapacityExceeded; } var record = try parseRecord(alloc, line); defer record.deinit(alloc); switch (record) { .coverage => |started_at_ms| { if (coverage_started_at_ms) |existing| { if (existing != started_at_ms) return error.InvalidUsageStore; } else { coverage_started_at_ms = started_at_ms; } }, .generation => |fact| { if (coverage_started_at_ms == null) return error.InvalidUsageStore; if (fact_indexes.getPtr(fact.id)) |indexes| { if (usage_report.GenerationFact.eql( facts.items[indexes.first], fact, ) or (indexes.second != null and usage_report.GenerationFact.eql( facts.items[indexes.second.?], fact, ))) {