| author | |
| committer | |
| log | b03345f32a5ba2849ddbeeae0e31e0e77ca01b01 |
| tree | 4100326d0b400a38525d980739214feaf48508b8 |
| parent | 5195b87639a1dc56b90751d0aecc4fcf4f2a1bb0 |
| parent | 3a63fa6b7f56a2f384ebd460e80c00e6bbd2efee |
| signature | Signed by PGP key 4AEE18F83AFDEB23 |
stage2: Add limited WASI support for selfExePath and globalCacheDir4 files changed, 80 insertions(+), 8 deletions(-)
lib/std/fs.zig+47| ... | ... | @@ -2547,6 +2547,15 @@ pub const SelfExePathError = os.ReadLinkError || os.SysCtlError || os.RealPathEr |
| 2547 | 2547 | /// `selfExePath` except allocates the result on the heap. |
| 2548 | 2548 | /// Caller owns returned memory. |
| 2549 | 2549 | pub fn selfExePathAlloc(allocator: Allocator) ![]u8 { |
| 2550 | if (builtin.os.tag == .wasi) { | |
| 2551 | var args = try std.process.argsWithAllocator(allocator); | |
| 2552 | defer args.deinit(); | |
| 2553 | // On WASI, argv[0] is always just the basename of the current executable | |
| 2554 | const exe_name = args.next() orelse return error.FileNotFound; | |
| 2555 | ||
| 2556 | var buf: [MAX_PATH_BYTES]u8 = undefined; | |
| 2557 | return allocator.dupe(u8, try selfExePathWasi(&buf, exe_name)); | |
| 2558 | } | |
| 2550 | 2559 | // Use of MAX_PATH_BYTES here is justified as, at least on one tested Linux |
| 2551 | 2560 | // system, readlink will completely fail to return a result larger than |
| 2552 | 2561 | // PATH_MAX even if given a sufficiently large buffer. This makes it |
| ... | ... | @@ -2643,10 +2652,48 @@ pub fn selfExePath(out_buffer: []u8) SelfExePathError![]u8 { |
| 2643 | 2652 | const end_index = std.unicode.utf16leToUtf8(out_buffer, utf16le_slice) catch unreachable; |
| 2644 | 2653 | return out_buffer[0..end_index]; |
| 2645 | 2654 | }, |
| 2655 | .wasi => @compileError("std.fs.selfExePath not supported for WASI. Use std.fs.selfExePathAlloc instead."), | |
| 2646 | 2656 | else => @compileError("std.fs.selfExePath not supported for this target"), |
| 2647 | 2657 | } |
| 2648 | 2658 | } |
| 2649 | 2659 | |
| 2660 | /// WASI-specific implementation of selfExePath | |
| 2661 | /// | |
| 2662 | /// On WASI argv0 is always just the executable basename, so this function relies | |
| 2663 | /// using a fixed executable directory path: "/zig" | |
| 2664 | /// | |
| 2665 | /// This path can be configured in wasmtime using `--mapdir=/zig::/path/to/zig/dir/` | |
| 2666 | fn selfExePathWasi(out_buffer: []u8, argv0: []const u8) SelfExePathError![]const u8 { | |
| 2667 | var allocator = std.heap.FixedBufferAllocator.init(out_buffer); | |
| 2668 | var alloc = allocator.allocator(); | |
| 2669 | ||
| 2670 | // Check these paths: | |
| 2671 | // 1. "/zig/{exe_name}" | |
| 2672 | // 2. "/zig/bin/{exe_name}" | |
| 2673 | const base_paths_to_check = &[_][]const u8{ "/zig", "/zig/bin" }; | |
| 2674 | const exe_names_to_check = &[_][]const u8{ path.basename(argv0), "zig.wasm" }; | |
| 2675 | ||
| 2676 | for (base_paths_to_check) |base_path| { | |
| 2677 | for (exe_names_to_check) |exe_name| { | |
| 2678 | const test_path = path.join(alloc, &.{ base_path, exe_name }) catch continue; | |
| 2679 | ||
| 2680 | // Make sure it's a file we're pointing to | |
| 2681 | const file = os.fstatat(os.wasi.AT.FDCWD, test_path, 0) catch continue; | |
| 2682 | if (file.filetype != .REGULAR_FILE) continue; | |
| 2683 | ||
| 2684 | // Path seems to be valid, let's try to turn it into an absolute path | |
| 2685 | var real_path_buf: [MAX_PATH_BYTES]u8 = undefined; | |
| 2686 | if (os.realpath(test_path, &real_path_buf)) |real_path| { | |
| 2687 | if (real_path.len > out_buffer.len) | |
| 2688 | return error.NameTooLong; | |
| 2689 | mem.copy(u8, out_buffer, real_path); | |
| 2690 | return out_buffer[0..real_path.len]; | |
| 2691 | } else |_| continue; | |
| 2692 | } | |
| 2693 | } | |
| 2694 | return error.FileNotFound; | |
| 2695 | } | |
| 2696 | ||
| 2650 | 2697 | /// The result is UTF16LE-encoded. |
| 2651 | 2698 | pub fn selfExePathW() [:0]const u16 { |
| 2652 | 2699 | const image_path_name = &os.windows.peb().ProcessParameters.ImagePathName; |
src/Cache.zig+11-7| ... | ... | @@ -762,18 +762,22 @@ pub const Manifest = struct { |
| 762 | 762 | |
| 763 | 763 | fn downgradeToSharedLock(self: *Manifest) !void { |
| 764 | 764 | if (!self.have_exclusive_lock) return; |
| 765 | const manifest_file = self.manifest_file.?; | |
| 766 | try manifest_file.downgradeLock(); | |
| 765 | if (std.process.can_spawn or !builtin.single_threaded) { // Some targets (WASI) do not support flock | |
| 766 | const manifest_file = self.manifest_file.?; | |
| 767 | try manifest_file.downgradeLock(); | |
| 768 | } | |
| 767 | 769 | self.have_exclusive_lock = false; |
| 768 | 770 | } |
| 769 | 771 | |
| 770 | 772 | fn upgradeToExclusiveLock(self: *Manifest) !void { |
| 771 | 773 | if (self.have_exclusive_lock) return; |
| 772 | const manifest_file = self.manifest_file.?; | |
| 773 | // Here we intentionally have a period where the lock is released, in case there are | |
| 774 | // other processes holding a shared lock. | |
| 775 | manifest_file.unlock(); | |
| 776 | try manifest_file.lock(.Exclusive); | |
| 774 | if (std.process.can_spawn or !builtin.single_threaded) { // Some targets (WASI) do not support flock | |
| 775 | const manifest_file = self.manifest_file.?; | |
| 776 | // Here we intentionally have a period where the lock is released, in case there are | |
| 777 | // other processes holding a shared lock. | |
| 778 | manifest_file.unlock(); | |
| 779 | try manifest_file.lock(.Exclusive); | |
| 780 | } | |
| 777 | 781 | self.have_exclusive_lock = true; |
| 778 | 782 | } |
| 779 | 783 |
src/introspect.zig+12-1| ... | ... | @@ -1,6 +1,7 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | 2 | const builtin = @import("builtin"); |
| 3 | 3 | const mem = std.mem; |
| 4 | const os = std.os; | |
| 4 | 5 | const fs = std.fs; |
| 5 | 6 | const Compilation = @import("Compilation.zig"); |
| 6 | 7 | |
| ... | ... | @@ -80,5 +81,15 @@ pub fn resolveGlobalCacheDir(allocator: mem.Allocator) ![]u8 { |
| 80 | 81 | } |
| 81 | 82 | } |
| 82 | 83 | |
| 83 | return fs.getAppDataDir(allocator, appname); | |
| 84 | if (builtin.os.tag == .wasi) { | |
| 85 | // On WASI, we have no way to get an App data dir, so we try to use a fixed | |
| 86 | // Preopen path "/cache" as a last resort | |
| 87 | const path = "/cache"; | |
| 88 | ||
| 89 | const file = os.fstatat(os.wasi.AT.FDCWD, path, 0) catch return error.CacheDirUnavailable; | |
| 90 | if (file.filetype != .DIRECTORY) return error.CacheDirUnavailable; | |
| 91 | return allocator.dupe(u8, path); | |
| 92 | } else { | |
| 93 | return fs.getAppDataDir(allocator, appname); | |
| 94 | } | |
| 84 | 95 | } |
src/main.zig+10| ... | ... | @@ -162,6 +162,16 @@ pub fn main() anyerror!void { |
| 162 | 162 | return mainArgs(gpa_tracy.allocator(), arena, args); |
| 163 | 163 | } |
| 164 | 164 | |
| 165 | // WASI: `--dir` instructs the WASM runtime to "preopen" a directory, making | |
| 166 | // it available to the us, the guest program. This is the only way for us to | |
| 167 | // access files/dirs on the host filesystem | |
| 168 | if (builtin.os.tag == .wasi) { | |
| 169 | // This sets our CWD to "/preopens/cwd" | |
| 170 | // Dot-prefixed preopens like `--dir=.` are "mounted" at "/preopens/cwd" | |
| 171 | // Other preopens like `--dir=lib` are "mounted" at "/" | |
| 172 | try std.os.initPreopensWasi(std.heap.page_allocator, "/preopens/cwd"); | |
| 173 | } | |
| 174 | ||
| 165 | 175 | return mainArgs(gpa, arena, args); |
| 166 | 176 | } |
| 167 | 177 |