| author | |
| committer | |
| log | 143127529b109971c931cce18cc7a328f6031df3 |
| tree | bacf6f1125a67270add4c7e1ec5f08376ed0a86d |
| parent | ec9dfc540b95a5577074e8915670ac2920b32f64 |
5 files changed, 59 insertions(+), 100 deletions(-)
lib/std/Io.zig+4| ... | ... | @@ -883,6 +883,10 @@ pub const Timestamp = struct { |
| 883 | 883 | return .{ .nanoseconds = t.nanoseconds, .clock = clock }; |
| 884 | 884 | } |
| 885 | 885 | |
| 886 | pub fn fromNanoseconds(x: i96) Timestamp { | |
| 887 | return .{ .nanoseconds = x }; | |
| 888 | } | |
| 889 | ||
| 886 | 890 | pub fn toSeconds(t: Timestamp) i64 { |
| 887 | 891 | return @intCast(@divTrunc(t.nanoseconds, std.time.ns_per_s)); |
| 888 | 892 | } |
lib/std/Io/Threaded.zig+47-16| ... | ... | @@ -167,7 +167,7 @@ pub fn io(t: *Threaded) Io { |
| 167 | 167 | |
| 168 | 168 | .dirMake = switch (builtin.os.tag) { |
| 169 | 169 | .windows => @panic("TODO"), |
| 170 | .wasi => @panic("TODO"), | |
| 170 | .wasi => dirMakeWasi, | |
| 171 | 171 | else => dirMakePosix, |
| 172 | 172 | }, |
| 173 | 173 | .dirStat = dirStat, |
| ... | ... | @@ -906,6 +906,37 @@ fn dirMakePosix(userdata: ?*anyopaque, dir: Io.Dir, sub_path: []const u8, mode: |
| 906 | 906 | } |
| 907 | 907 | } |
| 908 | 908 | |
| 909 | fn dirMakeWasi(userdata: ?*anyopaque, dir: Io.Dir, sub_path: []const u8, mode: Io.Dir.Mode) Io.Dir.MakeError!void { | |
| 910 | if (builtin.link_libc) return dirMakePosix(userdata, dir, sub_path, mode); | |
| 911 | const t: *Threaded = @ptrCast(@alignCast(userdata)); | |
| 912 | while (true) { | |
| 913 | try t.checkCancel(); | |
| 914 | switch (std.os.wasi.path_create_directory(dir.handle, sub_path.ptr, sub_path.len)) { | |
| 915 | .SUCCESS => return, | |
| 916 | .INTR => continue, | |
| 917 | .CANCELED => return error.Canceled, | |
| 918 | ||
| 919 | .ACCES => return error.AccessDenied, | |
| 920 | .BADF => |err| return errnoBug(err), | |
| 921 | .PERM => return error.PermissionDenied, | |
| 922 | .DQUOT => return error.DiskQuota, | |
| 923 | .EXIST => return error.PathAlreadyExists, | |
| 924 | .FAULT => |err| return errnoBug(err), | |
| 925 | .LOOP => return error.SymLinkLoop, | |
| 926 | .MLINK => return error.LinkQuotaExceeded, | |
| 927 | .NAMETOOLONG => return error.NameTooLong, | |
| 928 | .NOENT => return error.FileNotFound, | |
| 929 | .NOMEM => return error.SystemResources, | |
| 930 | .NOSPC => return error.NoSpaceLeft, | |
| 931 | .NOTDIR => return error.NotDir, | |
| 932 | .ROFS => return error.ReadOnlyFileSystem, | |
| 933 | .NOTCAPABLE => return error.AccessDenied, | |
| 934 | .ILSEQ => return error.BadPathName, | |
| 935 | else => |err| return posix.unexpectedErrno(err), | |
| 936 | } | |
| 937 | } | |
| 938 | } | |
| 939 | ||
| 909 | 940 | fn dirStat(userdata: ?*anyopaque, dir: Io.Dir) Io.Dir.StatError!Io.Dir.Stat { |
| 910 | 941 | const t: *Threaded = @ptrCast(@alignCast(userdata)); |
| 911 | 942 | try t.checkCancel(); |
| ... | ... | @@ -1005,13 +1036,13 @@ fn dirStatPathWasi( |
| 1005 | 1036 | const t: *Threaded = @ptrCast(@alignCast(userdata)); |
| 1006 | 1037 | const wasi = std.os.wasi; |
| 1007 | 1038 | const flags: wasi.lookupflags_t = .{ |
| 1008 | .SYMLINK_FOLLOW = @intFromBool(options.follow_symlinks), | |
| 1039 | .SYMLINK_FOLLOW = options.follow_symlinks, | |
| 1009 | 1040 | }; |
| 1010 | 1041 | var stat: wasi.filestat_t = undefined; |
| 1011 | 1042 | while (true) { |
| 1012 | 1043 | try t.checkCancel(); |
| 1013 | 1044 | switch (wasi.path_filestat_get(dir.handle, flags, sub_path.ptr, sub_path.len, &stat)) { |
| 1014 | .SUCCESS => return statFromWasi(stat), | |
| 1045 | .SUCCESS => return statFromWasi(&stat), | |
| 1015 | 1046 | .INTR => continue, |
| 1016 | 1047 | .CANCELED => return error.Canceled, |
| 1017 | 1048 | |
| ... | ... | @@ -1166,19 +1197,19 @@ fn dirAccessWasi( |
| 1166 | 1197 | userdata: ?*anyopaque, |
| 1167 | 1198 | dir: Io.Dir, |
| 1168 | 1199 | sub_path: []const u8, |
| 1169 | options: Io.File.OpenFlags, | |
| 1170 | ) Io.File.AccessError!void { | |
| 1200 | options: Io.Dir.AccessOptions, | |
| 1201 | ) Io.Dir.AccessError!void { | |
| 1171 | 1202 | if (builtin.link_libc) return dirAccessPosix(userdata, dir, sub_path, options); |
| 1172 | 1203 | const t: *Threaded = @ptrCast(@alignCast(userdata)); |
| 1173 | 1204 | const wasi = std.os.wasi; |
| 1174 | 1205 | const flags: wasi.lookupflags_t = .{ |
| 1175 | .SYMLINK_FOLLOW = @intFromBool(options.follow_symlinks), | |
| 1206 | .SYMLINK_FOLLOW = options.follow_symlinks, | |
| 1176 | 1207 | }; |
| 1177 | const stat = while (true) { | |
| 1178 | var stat: wasi.filestat_t = undefined; | |
| 1208 | var stat: wasi.filestat_t = undefined; | |
| 1209 | while (true) { | |
| 1179 | 1210 | try t.checkCancel(); |
| 1180 | 1211 | switch (wasi.path_filestat_get(dir.handle, flags, sub_path.ptr, sub_path.len, &stat)) { |
| 1181 | .SUCCESS => break statFromWasi(stat), | |
| 1212 | .SUCCESS => break, | |
| 1182 | 1213 | .INTR => continue, |
| 1183 | 1214 | .CANCELED => return error.Canceled, |
| 1184 | 1215 | |
| ... | ... | @@ -1194,9 +1225,9 @@ fn dirAccessWasi( |
| 1194 | 1225 | .ILSEQ => return error.BadPathName, |
| 1195 | 1226 | else => |err| return posix.unexpectedErrno(err), |
| 1196 | 1227 | } |
| 1197 | }; | |
| 1228 | } | |
| 1198 | 1229 | |
| 1199 | if (!options.mode.read and !options.mode.write and !options.mode.execute) | |
| 1230 | if (!options.read and !options.write and !options.execute) | |
| 1200 | 1231 | return; |
| 1201 | 1232 | |
| 1202 | 1233 | var directory: wasi.fdstat_t = undefined; |
| ... | ... | @@ -1204,14 +1235,14 @@ fn dirAccessWasi( |
| 1204 | 1235 | return error.AccessDenied; |
| 1205 | 1236 | |
| 1206 | 1237 | var rights: wasi.rights_t = .{}; |
| 1207 | if (options.mode.read) { | |
| 1238 | if (options.read) { | |
| 1208 | 1239 | if (stat.filetype == .DIRECTORY) { |
| 1209 | 1240 | rights.FD_READDIR = true; |
| 1210 | 1241 | } else { |
| 1211 | 1242 | rights.FD_READ = true; |
| 1212 | 1243 | } |
| 1213 | 1244 | } |
| 1214 | if (options.mode.write) | |
| 1245 | if (options.write) | |
| 1215 | 1246 | rights.FD_WRITE = true; |
| 1216 | 1247 | |
| 1217 | 1248 | // No validation for execution. |
| ... | ... | @@ -3262,9 +3293,9 @@ fn statFromWasi(st: *const std.os.wasi.filestat_t) Io.File.Stat { |
| 3262 | 3293 | .SOCKET_STREAM, .SOCKET_DGRAM => .unix_domain_socket, |
| 3263 | 3294 | else => .unknown, |
| 3264 | 3295 | }, |
| 3265 | .atime = st.atim, | |
| 3266 | .mtime = st.mtim, | |
| 3267 | .ctime = st.ctim, | |
| 3296 | .atime = .fromNanoseconds(st.atim), | |
| 3297 | .mtime = .fromNanoseconds(st.mtim), | |
| 3298 | .ctime = .fromNanoseconds(st.ctim), | |
| 3268 | 3299 | }; |
| 3269 | 3300 | } |
| 3270 | 3301 |
lib/std/os.zig+7-1| ... | ... | @@ -201,7 +201,13 @@ pub fn getFdPath(fd: std.posix.fd_t, out_buffer: *[max_path_bytes]u8) std.posix. |
| 201 | 201 | } |
| 202 | 202 | } |
| 203 | 203 | |
| 204 | pub fn fstat_wasi(fd: posix.fd_t) posix.FStatError!wasi.filestat_t { | |
| 204 | pub const FstatError = error{ | |
| 205 | SystemResources, | |
| 206 | AccessDenied, | |
| 207 | Unexpected, | |
| 208 | }; | |
| 209 | ||
| 210 | pub fn fstat_wasi(fd: posix.fd_t) FstatError!wasi.filestat_t { | |
| 205 | 211 | var stat: wasi.filestat_t = undefined; |
| 206 | 212 | switch (wasi.fd_filestat_get(fd, &stat)) { |
| 207 | 213 | .SUCCESS => return stat, |
lib/std/posix.zig+1-25| ... | ... | @@ -2809,37 +2809,13 @@ pub fn mkdirat(dir_fd: fd_t, sub_dir_path: []const u8, mode: mode_t) MakeDirErro |
| 2809 | 2809 | const sub_dir_path_w = try windows.sliceToPrefixedFileW(dir_fd, sub_dir_path); |
| 2810 | 2810 | return mkdiratW(dir_fd, sub_dir_path_w.span(), mode); |
| 2811 | 2811 | } else if (native_os == .wasi and !builtin.link_libc) { |
| 2812 | return mkdiratWasi(dir_fd, sub_dir_path, mode); | |
| 2812 | @compileError("use std.Io instead"); | |
| 2813 | 2813 | } else { |
| 2814 | 2814 | const sub_dir_path_c = try toPosixPath(sub_dir_path); |
| 2815 | 2815 | return mkdiratZ(dir_fd, &sub_dir_path_c, mode); |
| 2816 | 2816 | } |
| 2817 | 2817 | } |
| 2818 | 2818 | |
| 2819 | pub fn mkdiratWasi(dir_fd: fd_t, sub_dir_path: []const u8, mode: mode_t) MakeDirError!void { | |
| 2820 | _ = mode; | |
| 2821 | switch (wasi.path_create_directory(dir_fd, sub_dir_path.ptr, sub_dir_path.len)) { | |
| 2822 | .SUCCESS => return, | |
| 2823 | .ACCES => return error.AccessDenied, | |
| 2824 | .BADF => unreachable, | |
| 2825 | .PERM => return error.PermissionDenied, | |
| 2826 | .DQUOT => return error.DiskQuota, | |
| 2827 | .EXIST => return error.PathAlreadyExists, | |
| 2828 | .FAULT => unreachable, | |
| 2829 | .LOOP => return error.SymLinkLoop, | |
| 2830 | .MLINK => return error.LinkQuotaExceeded, | |
| 2831 | .NAMETOOLONG => return error.NameTooLong, | |
| 2832 | .NOENT => return error.FileNotFound, | |
| 2833 | .NOMEM => return error.SystemResources, | |
| 2834 | .NOSPC => return error.NoSpaceLeft, | |
| 2835 | .NOTDIR => return error.NotDir, | |
| 2836 | .ROFS => return error.ReadOnlyFileSystem, | |
| 2837 | .NOTCAPABLE => return error.AccessDenied, | |
| 2838 | .ILSEQ => return error.BadPathName, | |
| 2839 | else => |err| return unexpectedErrno(err), | |
| 2840 | } | |
| 2841 | } | |
| 2842 | ||
| 2843 | 2819 | /// Same as `mkdirat` except the parameters are null-terminated. |
| 2844 | 2820 | pub fn mkdiratZ(dir_fd: fd_t, sub_dir_path: [*:0]const u8, mode: mode_t) MakeDirError!void { |
| 2845 | 2821 | if (native_os == .windows) { |
lib/std/posix/test.zig-58| ... | ... | @@ -109,64 +109,6 @@ test "open smoke test" { |
| 109 | 109 | } |
| 110 | 110 | } |
| 111 | 111 | |
| 112 | test "openat smoke test" { | |
| 113 | if (native_os == .windows) return error.SkipZigTest; | |
| 114 | ||
| 115 | // TODO verify file attributes using `fstatat` | |
| 116 | ||
| 117 | var tmp = tmpDir(.{}); | |
| 118 | defer tmp.cleanup(); | |
| 119 | ||
| 120 | var fd: posix.fd_t = undefined; | |
| 121 | const mode: posix.mode_t = if (native_os == .windows) 0 else 0o666; | |
| 122 | ||
| 123 | // Create some file using `openat`. | |
| 124 | fd = try posix.openat(tmp.dir.fd, "some_file", CommonOpenFlags.lower(.{ | |
| 125 | .ACCMODE = .RDWR, | |
| 126 | .CREAT = true, | |
| 127 | .EXCL = true, | |
| 128 | }), mode); | |
| 129 | posix.close(fd); | |
| 130 | ||
| 131 | // Try this again with the same flags. This op should fail with error.PathAlreadyExists. | |
| 132 | try expectError(error.PathAlreadyExists, posix.openat(tmp.dir.fd, "some_file", CommonOpenFlags.lower(.{ | |
| 133 | .ACCMODE = .RDWR, | |
| 134 | .CREAT = true, | |
| 135 | .EXCL = true, | |
| 136 | }), mode)); | |
| 137 | ||
| 138 | // Try opening without `EXCL` flag. | |
| 139 | fd = try posix.openat(tmp.dir.fd, "some_file", CommonOpenFlags.lower(.{ | |
| 140 | .ACCMODE = .RDWR, | |
| 141 | .CREAT = true, | |
| 142 | }), mode); | |
| 143 | posix.close(fd); | |
| 144 | ||
| 145 | // Try opening as a directory which should fail. | |
| 146 | try expectError(error.NotDir, posix.openat(tmp.dir.fd, "some_file", CommonOpenFlags.lower(.{ | |
| 147 | .ACCMODE = .RDWR, | |
| 148 | .DIRECTORY = true, | |
| 149 | }), mode)); | |
| 150 | ||
| 151 | // Create some directory | |
| 152 | try posix.mkdirat(tmp.dir.fd, "some_dir", mode); | |
| 153 | ||
| 154 | // Open dir using `open` | |
| 155 | fd = try posix.openat(tmp.dir.fd, "some_dir", CommonOpenFlags.lower(.{ | |
| 156 | .ACCMODE = .RDONLY, | |
| 157 | .DIRECTORY = true, | |
| 158 | }), mode); | |
| 159 | posix.close(fd); | |
| 160 | ||
| 161 | // Try opening as file which should fail (skip on wasi+libc due to | |
| 162 | // https://github.com/bytecodealliance/wasmtime/issues/9054) | |
| 163 | if (native_os != .wasi or !builtin.link_libc) { | |
| 164 | try expectError(error.IsDir, posix.openat(tmp.dir.fd, "some_dir", CommonOpenFlags.lower(.{ | |
| 165 | .ACCMODE = .RDWR, | |
| 166 | }), mode)); | |
| 167 | } | |
| 168 | } | |
| 169 | ||
| 170 | 112 | test "readlink on Windows" { |
| 171 | 113 | if (native_os != .windows) return error.SkipZigTest; |
| 172 | 114 |