| ... | @@ -9,7 +9,6 @@ const Allocator = mem.Allocator; | ... | @@ -9,7 +9,6 @@ const Allocator = mem.Allocator; |
| 9 | const wasi = std.os.wasi; | 9 | const wasi = std.os.wasi; |
| 10 | const fd_t = wasi.fd_t; | 10 | const fd_t = wasi.fd_t; |
| 11 | const prestat_t = wasi.prestat_t; | 11 | const prestat_t = wasi.prestat_t; |
| 12 | const testing = std.testing; | | |
| 13 | | 12 | |
| 14 | pub const Preopens = struct { | 13 | pub const Preopens = struct { |
| 15 | // Indexed by file descriptor number. | 14 | // Indexed by file descriptor number. |
| ... | @@ -23,30 +22,6 @@ pub const Preopens = struct { | ... | @@ -23,30 +22,6 @@ pub const Preopens = struct { |
| 23 | } | 22 | } |
| 24 | return null; | 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 | pub fn preopensAlloc(gpa: Allocator) Allocator.Error!Preopens { | 27 | pub fn preopensAlloc(gpa: Allocator) Allocator.Error!Preopens { |
| ... | @@ -79,86 +54,3 @@ pub fn preopensAlloc(gpa: Allocator) Allocator.Error!Preopens { | ... | @@ -79,86 +54,3 @@ pub fn preopensAlloc(gpa: Allocator) Allocator.Error!Preopens { |
| 79 | names.appendAssumeCapacity(name); | 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 | } | | |