| ... | ... | @@ -9,7 +9,6 @@ const Allocator = mem.Allocator; |
| 9 | 9 | const wasi = std.os.wasi; |
| 10 | 10 | const fd_t = wasi.fd_t; |
| 11 | 11 | const prestat_t = wasi.prestat_t; |
| 12 | | const testing = std.testing; |
| 13 | 12 | |
| 14 | 13 | pub const Preopens = struct { |
| 15 | 14 | // Indexed by file descriptor number. |
| ... | ... | @@ -23,30 +22,6 @@ pub const Preopens = struct { |
| 23 | 22 | } |
| 24 | 23 | return null; |
| 25 | 24 | } |
| 26 | | |
| 27 | | pub fn findDir(p: Preopens, full_path: []const u8, flags: std.fs.Dir.OpenDirOptions) std.fs.Dir.OpenError!std.fs.Dir { |
| 28 | | if (p.names.len <= 2) |
| 29 | | return std.fs.Dir.OpenError.BadPathName; // there are no preopens |
| 30 | | |
| 31 | | var prefix: []const u8 = ""; |
| 32 | | var fd: usize = 0; |
| 33 | | for (p.names) |preopen, i| { |
| 34 | | if (i > 2 and wasiPathPrefixMatches(preopen, full_path)) { |
| 35 | | if (preopen.len > prefix.len) { |
| 36 | | prefix = preopen; |
| 37 | | fd = i; |
| 38 | | } |
| 39 | | } |
| 40 | | } |
| 41 | | |
| 42 | | // still no match |
| 43 | | if (fd == 0) { |
| 44 | | return std.fs.Dir.OpenError.FileNotFound; |
| 45 | | } |
| 46 | | const d = std.fs.Dir{ .fd = @intCast(os.fd_t, fd) }; |
| 47 | | const rel = full_path[prefix.len + 1 .. full_path.len]; |
| 48 | | return d.openDirWasi(rel, flags); |
| 49 | | } |
| 50 | 25 | }; |
| 51 | 26 | |
| 52 | 27 | pub fn preopensAlloc(gpa: Allocator) Allocator.Error!Preopens { |
| ... | ... | @@ -79,86 +54,3 @@ pub fn preopensAlloc(gpa: Allocator) Allocator.Error!Preopens { |
| 79 | 54 | names.appendAssumeCapacity(name); |
| 80 | 55 | } |
| 81 | 56 | } |
| 82 | | |
| 83 | | fn wasiPathPrefixMatches(prefix: []const u8, path: []const u8) bool { |
| 84 | | if (path[0] != '/' and prefix.len == 0) |
| 85 | | return true; |
| 86 | | |
| 87 | | if (path.len < prefix.len) |
| 88 | | return false; |
| 89 | | |
| 90 | | if (prefix.len == 1) { |
| 91 | | return prefix[0] == path[0]; |
| 92 | | } |
| 93 | | |
| 94 | | if (!std.mem.eql(u8, path[0..prefix.len], prefix)) { |
| 95 | | return false; |
| 96 | | } |
| 97 | | |
| 98 | | return path.len == prefix.len or |
| 99 | | path[prefix.len] == '/'; |
| 100 | | } |
| 101 | | |
| 102 | | test "preopens" { |
| 103 | | if (builtin.os.tag != .wasi or builtin.link_libc) return error.SkipZigTest; |
| 104 | | |
| 105 | | // lifted from `testing` |
| 106 | | const random_bytes_count = 12; |
| 107 | | const buf_size = 256; |
| 108 | | const path = "/tmp"; |
| 109 | | const tmp_file_name = "file.txt"; |
| 110 | | const nonsense = "nonsense"; |
| 111 | | |
| 112 | | var random_bytes: [random_bytes_count]u8 = undefined; |
| 113 | | var buf: [buf_size]u8 = undefined; |
| 114 | | |
| 115 | | std.crypto.random.bytes(&random_bytes); |
| 116 | | const sub_path = std.fs.base64_encoder.encode(&buf, &random_bytes); |
| 117 | | |
| 118 | | // find all preopens |
| 119 | | const allocator = std.heap.page_allocator; |
| 120 | | var wasi_preopens = try std.fs.wasi.preopensAlloc(allocator); |
| 121 | | |
| 122 | | // look for the exact "/tmp" preopen match |
| 123 | | const fd = std.fs.wasi.Preopens.find(wasi_preopens, path) orelse unreachable; |
| 124 | | const base_dir = std.fs.Dir{ .fd = fd }; |
| 125 | | |
| 126 | | var tmp_path = base_dir.makeOpenPath(sub_path, .{}) catch |
| 127 | | @panic("unable to make tmp dir for testing: /tmp/<rand-path>"); |
| 128 | | |
| 129 | | defer tmp_path.close(); |
| 130 | | defer tmp_path.deleteTree(sub_path) catch {}; |
| 131 | | |
| 132 | | // create a file under /tmp/<rand>/file.txt with contents "nonsense" |
| 133 | | try tmp_path.writeFile(tmp_file_name, nonsense); |
| 134 | | |
| 135 | | // now look for the file as a single path |
| 136 | | var tmp_dir_path_buf: [buf_size]u8 = undefined; |
| 137 | | const tmp_dir_path = try std.fmt.bufPrint(&tmp_dir_path_buf, "{s}/{s}", .{ path, sub_path }); |
| 138 | | |
| 139 | | // find "/tmp/<rand>" using `findDir()` |
| 140 | | const tmp_file_dir = try wasi_preopens.findDir(tmp_dir_path, .{}); |
| 141 | | |
| 142 | | const text = try tmp_file_dir.readFile(tmp_file_name, &buf); |
| 143 | | |
| 144 | | // ensure the file contents match "nonsense" |
| 145 | | try testing.expect(std.mem.eql(u8, nonsense, text)); |
| 146 | | } |
| 147 | | |
| 148 | | test "wasiPathPrefixMatches" { |
| 149 | | try testing.expect(wasiPathPrefixMatches("/", "/foo")); |
| 150 | | try testing.expect(wasiPathPrefixMatches("/testcases", "/testcases/test.txt")); |
| 151 | | try testing.expect(wasiPathPrefixMatches("", "foo")); |
| 152 | | try testing.expect(wasiPathPrefixMatches("foo", "foo")); |
| 153 | | try testing.expect(wasiPathPrefixMatches("foo", "foo/bar")); |
| 154 | | try testing.expect(!wasiPathPrefixMatches("bar", "foo/bar")); |
| 155 | | try testing.expect(!wasiPathPrefixMatches("bar", "foo")); |
| 156 | | try testing.expect(wasiPathPrefixMatches("foo", "foo/bar")); |
| 157 | | try testing.expect(!wasiPathPrefixMatches("fooo", "foo")); |
| 158 | | try testing.expect(!wasiPathPrefixMatches("foo", "fooo")); |
| 159 | | try testing.expect(!wasiPathPrefixMatches("foo/bar", "foo")); |
| 160 | | try testing.expect(!wasiPathPrefixMatches("bar/foo", "foo")); |
| 161 | | try testing.expect(wasiPathPrefixMatches("/foo", "/foo")); |
| 162 | | try testing.expect(wasiPathPrefixMatches("/foo", "/foo")); |
| 163 | | try testing.expect(wasiPathPrefixMatches("/foo", "/foo/")); |
| 164 | | } |