windows: get +ssh-cache building on Windows (#7947)

There are still problems linking due to `gettext`. No idea if this
actually _works_ on Windows. File locking had to be disabled on Windows
because of a bug in the Zig std library. Adding all of the explicit
error sets happened due to disabling file locking. Fixing permissions
had to be disabled on Windows as the Windows file system does not
support permissions in the way that POSIX systems like macOS and Linux
do.
This commit is contained in:
Mitchell Hashimoto
2025-07-14 19:55:56 -07:00
committed by GitHub

View File

@ -4,6 +4,7 @@
const DiskCache = @This(); const DiskCache = @This();
const std = @import("std"); const std = @import("std");
const builtin = @import("builtin");
const assert = std.debug.assert; const assert = std.debug.assert;
const Allocator = std.mem.Allocator; const Allocator = std.mem.Allocator;
const xdg = @import("../../os/main.zig").xdg; const xdg = @import("../../os/main.zig").xdg;
@ -56,6 +57,8 @@ pub fn clear(self: DiskCache) !void {
pub const AddResult = enum { added, updated }; pub const AddResult = enum { added, updated };
pub const AddError = std.fs.Dir.MakeError || std.fs.File.OpenError || std.fs.File.LockError || std.fs.File.ReadError || std.fs.File.WriteError || std.posix.RealPathError || std.posix.RenameError || Allocator.Error || error{ HostnameIsInvalid, CacheIsLocked };
/// Add or update a hostname entry in the cache. /// Add or update a hostname entry in the cache.
/// Returns AddResult.added for new entries or AddResult.updated for existing ones. /// Returns AddResult.added for new entries or AddResult.updated for existing ones.
/// The cache file is created if it doesn't exist with secure permissions (0600). /// The cache file is created if it doesn't exist with secure permissions (0600).
@ -63,7 +66,7 @@ pub fn add(
self: DiskCache, self: DiskCache,
alloc: Allocator, alloc: Allocator,
hostname: []const u8, hostname: []const u8,
) !AddResult { ) AddError!AddResult {
if (!isValidCacheKey(hostname)) return error.HostnameIsInvalid; if (!isValidCacheKey(hostname)) return error.HostnameIsInvalid;
// Create cache directory if needed // Create cache directory if needed
@ -94,8 +97,10 @@ pub fn add(
defer file.close(); defer file.close();
// Lock // Lock
_ = file.tryLock(.exclusive) catch return error.CacheIsLocked; // Causes a compile failure in the Zig std library on Windows, see:
defer file.unlock(); // https://github.com/ziglang/zig/issues/18430
if (comptime builtin.os.tag != .windows) _ = file.tryLock(.exclusive) catch return error.CacheIsLocked;
defer if (comptime builtin.os.tag != .windows) file.unlock();
var entries = try readEntries(alloc, file); var entries = try readEntries(alloc, file);
defer deinitEntries(alloc, &entries); defer deinitEntries(alloc, &entries);
@ -125,13 +130,15 @@ pub fn add(
return result; return result;
} }
pub const RemoveError = std.fs.Dir.OpenError || std.fs.File.OpenError || std.fs.File.ReadError || std.fs.File.WriteError || std.posix.RealPathError || std.posix.RenameError || Allocator.Error || error{ HostnameIsInvalid, CacheIsLocked };
/// Remove a hostname entry from the cache. /// Remove a hostname entry from the cache.
/// No error is returned if the hostname doesn't exist or the cache file is missing. /// No error is returned if the hostname doesn't exist or the cache file is missing.
pub fn remove( pub fn remove(
self: DiskCache, self: DiskCache,
alloc: Allocator, alloc: Allocator,
hostname: []const u8, hostname: []const u8,
) !void { ) RemoveError!void {
if (!isValidCacheKey(hostname)) return error.HostnameIsInvalid; if (!isValidCacheKey(hostname)) return error.HostnameIsInvalid;
// Open our file // Open our file
@ -145,9 +152,11 @@ pub fn remove(
defer file.close(); defer file.close();
try fixupPermissions(file); try fixupPermissions(file);
// Acquire exclusive lock // Lock
_ = file.tryLock(.exclusive) catch return error.CacheIsLocked; // Causes a compile failure in the Zig std library on Windows, see:
defer file.unlock(); // https://github.com/ziglang/zig/issues/18430
if (comptime builtin.os.tag != .windows) _ = file.tryLock(.exclusive) catch return error.CacheIsLocked;
defer if (comptime builtin.os.tag != .windows) file.unlock();
// Read existing entries // Read existing entries
var entries = try readEntries(alloc, file); var entries = try readEntries(alloc, file);
@ -191,6 +200,9 @@ pub fn contains(
} }
fn fixupPermissions(file: std.fs.File) !void { fn fixupPermissions(file: std.fs.File) !void {
// Windows does not support chmod
if (comptime builtin.os.tag == .windows) return;
// Ensure file has correct permissions (readable/writable by // Ensure file has correct permissions (readable/writable by
// owner only) // owner only)
const stat = try file.stat(); const stat = try file.stat();
@ -199,12 +211,14 @@ fn fixupPermissions(file: std.fs.File) !void {
} }
} }
pub const WriteCacheFileError = std.fs.Dir.OpenError || std.fs.File.OpenError || std.fs.File.WriteError || std.fs.Dir.RealPathAllocError || std.posix.RealPathError || std.posix.RenameError || error{FileTooBig};
fn writeCacheFile( fn writeCacheFile(
self: DiskCache, self: DiskCache,
alloc: Allocator, alloc: Allocator,
entries: std.StringHashMap(Entry), entries: std.StringHashMap(Entry),
expire_days: ?u32, expire_days: ?u32,
) !void { ) WriteCacheFileError!void {
var td: TempDir = try .init(); var td: TempDir = try .init();
defer td.deinit(); defer td.deinit();
@ -264,7 +278,7 @@ pub fn deinitEntries(
fn readEntries( fn readEntries(
alloc: Allocator, alloc: Allocator,
file: std.fs.File, file: std.fs.File,
) !std.StringHashMap(Entry) { ) (std.fs.File.ReadError || Allocator.Error || error{FileTooBig})!std.StringHashMap(Entry) {
const content = try file.readToEndAlloc(alloc, MAX_CACHE_SIZE); const content = try file.readToEndAlloc(alloc, MAX_CACHE_SIZE);
defer alloc.free(content); defer alloc.free(content);