| author | |
| committer | |
| log | f8ea00bd6dccba678901b50904972c017c9e66a1 |
| tree | 1a5f6f077f1442325896b1f48e827f72a268e9ff |
| parent | 3bf0ce65a514e6f86241364c4a11089e32b3ba57 |
8 files changed, 225 insertions(+), 226 deletions(-)
lib/std/Io.zig+1| ... | ... | @@ -663,6 +663,7 @@ pub const VTable = struct { |
| 663 | 663 | dirMake: *const fn (?*anyopaque, Dir, sub_path: []const u8, mode: Dir.Mode) Dir.MakeError!void, |
| 664 | 664 | dirStat: *const fn (?*anyopaque, Dir) Dir.StatError!Dir.Stat, |
| 665 | 665 | dirStatPath: *const fn (?*anyopaque, Dir, sub_path: []const u8, Dir.StatPathOptions) Dir.StatPathError!File.Stat, |
| 666 | dirAccess: *const fn (?*anyopaque, Dir, sub_path: []const u8, Dir.AccessOptions) Dir.AccessError!void, | |
| 666 | 667 | dirCreateFile: *const fn (?*anyopaque, Dir, sub_path: []const u8, File.CreateFlags) File.OpenError!File, |
| 667 | 668 | dirOpenFile: *const fn (?*anyopaque, Dir, sub_path: []const u8, File.OpenFlags) File.OpenError!File, |
| 668 | 669 | fileStat: *const fn (?*anyopaque, File) File.StatError!File.Stat, |
lib/std/Io/Dir.zig+31| ... | ... | @@ -23,6 +23,37 @@ pub const PathNameError = error{ |
| 23 | 23 | BadPathName, |
| 24 | 24 | }; |
| 25 | 25 | |
| 26 | pub const AccessError = error{ | |
| 27 | AccessDenied, | |
| 28 | PermissionDenied, | |
| 29 | FileNotFound, | |
| 30 | InputOutput, | |
| 31 | SystemResources, | |
| 32 | FileBusy, | |
| 33 | SymLinkLoop, | |
| 34 | ReadOnlyFileSystem, | |
| 35 | } || PathNameError || Io.Cancelable || Io.UnexpectedError; | |
| 36 | ||
| 37 | pub const AccessOptions = packed struct { | |
| 38 | follow_symlinks: bool = true, | |
| 39 | read: bool = false, | |
| 40 | write: bool = false, | |
| 41 | execute: bool = false, | |
| 42 | }; | |
| 43 | ||
| 44 | /// Test accessing `sub_path`. | |
| 45 | /// | |
| 46 | /// On Windows, `sub_path` should be encoded as [WTF-8](https://wtf-8.codeberg.page/). | |
| 47 | /// On WASI, `sub_path` should be encoded as valid UTF-8. | |
| 48 | /// On other platforms, `sub_path` is an opaque sequence of bytes with no particular encoding. | |
| 49 | /// | |
| 50 | /// Be careful of Time-Of-Check-Time-Of-Use race conditions when using this | |
| 51 | /// function. For example, instead of testing if a file exists and then opening | |
| 52 | /// it, just open it and handle the error for file not found. | |
| 53 | pub fn access(dir: Dir, io: Io, sub_path: []const u8, options: AccessOptions) AccessError!void { | |
| 54 | return io.vtable.dirAccess(io.userdata, dir, sub_path, options); | |
| 55 | } | |
| 56 | ||
| 26 | 57 | pub const OpenError = error{ |
| 27 | 58 | FileNotFound, |
| 28 | 59 | NotDir, |
lib/std/Io/File.zig+61-1| ... | ... | @@ -80,7 +80,67 @@ pub fn stat(file: File, io: Io) StatError!Stat { |
| 80 | 80 | return io.vtable.fileStat(io.userdata, file); |
| 81 | 81 | } |
| 82 | 82 | |
| 83 | pub const OpenFlags = std.fs.File.OpenFlags; | |
| 83 | pub const OpenMode = enum { | |
| 84 | read_only, | |
| 85 | write_only, | |
| 86 | read_write, | |
| 87 | }; | |
| 88 | ||
| 89 | pub const Lock = enum { | |
| 90 | none, | |
| 91 | shared, | |
| 92 | exclusive, | |
| 93 | }; | |
| 94 | ||
| 95 | pub const OpenFlags = struct { | |
| 96 | mode: OpenMode = .read_only, | |
| 97 | ||
| 98 | /// Open the file with an advisory lock to coordinate with other processes | |
| 99 | /// accessing it at the same time. An exclusive lock will prevent other | |
| 100 | /// processes from acquiring a lock. A shared lock will prevent other | |
| 101 | /// processes from acquiring a exclusive lock, but does not prevent | |
| 102 | /// other process from getting their own shared locks. | |
| 103 | /// | |
| 104 | /// The lock is advisory, except on Linux in very specific circumstances[1]. | |
| 105 | /// This means that a process that does not respect the locking API can still get access | |
| 106 | /// to the file, despite the lock. | |
| 107 | /// | |
| 108 | /// On these operating systems, the lock is acquired atomically with | |
| 109 | /// opening the file: | |
| 110 | /// * Darwin | |
| 111 | /// * DragonFlyBSD | |
| 112 | /// * FreeBSD | |
| 113 | /// * Haiku | |
| 114 | /// * NetBSD | |
| 115 | /// * OpenBSD | |
| 116 | /// On these operating systems, the lock is acquired via a separate syscall | |
| 117 | /// after opening the file: | |
| 118 | /// * Linux | |
| 119 | /// * Windows | |
| 120 | /// | |
| 121 | /// [1]: https://www.kernel.org/doc/Documentation/filesystems/mandatory-locking.txt | |
| 122 | lock: Lock = .none, | |
| 123 | ||
| 124 | /// Sets whether or not to wait until the file is locked to return. If set to true, | |
| 125 | /// `error.WouldBlock` will be returned. Otherwise, the file will wait until the file | |
| 126 | /// is available to proceed. | |
| 127 | lock_nonblocking: bool = false, | |
| 128 | ||
| 129 | /// Set this to allow the opened file to automatically become the | |
| 130 | /// controlling TTY for the current process. | |
| 131 | allow_ctty: bool = false, | |
| 132 | ||
| 133 | follow_symlinks: bool = true, | |
| 134 | ||
| 135 | pub fn isRead(self: OpenFlags) bool { | |
| 136 | return self.mode != .write_only; | |
| 137 | } | |
| 138 | ||
| 139 | pub fn isWrite(self: OpenFlags) bool { | |
| 140 | return self.mode != .read_only; | |
| 141 | } | |
| 142 | }; | |
| 143 | ||
| 84 | 144 | pub const CreateFlags = std.fs.File.CreateFlags; |
| 85 | 145 | |
| 86 | 146 | pub const OpenError = error{ |
lib/std/Io/Threaded.zig+113-5| ... | ... | @@ -183,6 +183,11 @@ pub fn io(t: *Threaded) Io { |
| 183 | 183 | .wasi => fileStatWasi, |
| 184 | 184 | else => fileStatPosix, |
| 185 | 185 | }, |
| 186 | .dirAccess = switch (builtin.os.tag) { | |
| 187 | .windows => @panic("TODO"), | |
| 188 | .wasi => dirAccessWasi, | |
| 189 | else => dirAccessPosix, | |
| 190 | }, | |
| 186 | 191 | .dirCreateFile = switch (builtin.os.tag) { |
| 187 | 192 | .windows => @panic("TODO"), |
| 188 | 193 | .wasi => @panic("TODO"), |
| ... | ... | @@ -992,7 +997,6 @@ fn dirStatPathWasi( |
| 992 | 997 | ) Io.Dir.StatPathError!Io.File.Stat { |
| 993 | 998 | if (builtin.link_libc) return dirStatPathPosix(userdata, dir, sub_path, options); |
| 994 | 999 | const t: *Threaded = @ptrCast(@alignCast(userdata)); |
| 995 | const dir_fd = dir.handle; | |
| 996 | 1000 | const wasi = std.os.wasi; |
| 997 | 1001 | const flags: wasi.lookupflags_t = .{ |
| 998 | 1002 | .SYMLINK_FOLLOW = @intFromBool(options.follow_symlinks), |
| ... | ... | @@ -1000,16 +1004,16 @@ fn dirStatPathWasi( |
| 1000 | 1004 | var stat: wasi.filestat_t = undefined; |
| 1001 | 1005 | while (true) { |
| 1002 | 1006 | try t.checkCancel(); |
| 1003 | switch (wasi.path_filestat_get(dir_fd, flags, sub_path.ptr, sub_path.len, &stat)) { | |
| 1007 | switch (wasi.path_filestat_get(dir.handle, flags, sub_path.ptr, sub_path.len, &stat)) { | |
| 1004 | 1008 | .SUCCESS => return statFromWasi(stat), |
| 1005 | 1009 | .INTR => continue, |
| 1006 | 1010 | .CANCELED => return error.Canceled, |
| 1007 | 1011 | |
| 1008 | .INVAL => |err| errnoBug(err), | |
| 1009 | .BADF => |err| errnoBug(err), // Always a race condition. | |
| 1012 | .INVAL => |err| return errnoBug(err), | |
| 1013 | .BADF => |err| return errnoBug(err), // Always a race condition. | |
| 1010 | 1014 | .NOMEM => return error.SystemResources, |
| 1011 | 1015 | .ACCES => return error.AccessDenied, |
| 1012 | .FAULT => |err| errnoBug(err), | |
| 1016 | .FAULT => |err| return errnoBug(err), | |
| 1013 | 1017 | .NAMETOOLONG => return error.NameTooLong, |
| 1014 | 1018 | .NOENT => return error.FileNotFound, |
| 1015 | 1019 | .NOTDIR => return error.FileNotFound, |
| ... | ... | @@ -1103,6 +1107,110 @@ const fstatat_sym = if (posix.lfs64_abi) posix.system.fstatat64 else posix.syste |
| 1103 | 1107 | const lseek_sym = if (posix.lfs64_abi) posix.system.lseek64 else posix.system.lseek; |
| 1104 | 1108 | const preadv_sym = if (posix.lfs64_abi) posix.system.preadv64 else posix.system.preadv; |
| 1105 | 1109 | |
| 1110 | fn dirAccessPosix( | |
| 1111 | userdata: ?*anyopaque, | |
| 1112 | dir: Io.Dir, | |
| 1113 | sub_path: []const u8, | |
| 1114 | options: Io.Dir.AccessOptions, | |
| 1115 | ) Io.Dir.AccessError!void { | |
| 1116 | const t: *Threaded = @ptrCast(@alignCast(userdata)); | |
| 1117 | ||
| 1118 | var path_buffer: [posix.PATH_MAX]u8 = undefined; | |
| 1119 | const sub_path_posix = try pathToPosix(sub_path, &path_buffer); | |
| 1120 | ||
| 1121 | const flags: u32 = @as(u32, if (!options.follow_symlinks) posix.AT.SYMLINK_NOFOLLOW else 0); | |
| 1122 | ||
| 1123 | const mode: u32 = | |
| 1124 | @as(u32, if (options.read) posix.R_OK else 0) | | |
| 1125 | @as(u32, if (options.write) posix.W_OK else 0) | | |
| 1126 | @as(u32, if (options.execute) posix.X_OK else 0); | |
| 1127 | ||
| 1128 | while (true) { | |
| 1129 | try t.checkCancel(); | |
| 1130 | switch (posix.errno(posix.system.faccessat(dir.handle, sub_path_posix, mode, flags))) { | |
| 1131 | .SUCCESS => return, | |
| 1132 | .INTR => continue, | |
| 1133 | .CANCELED => return error.Canceled, | |
| 1134 | ||
| 1135 | .ACCES => return error.AccessDenied, | |
| 1136 | .PERM => return error.PermissionDenied, | |
| 1137 | .ROFS => return error.ReadOnlyFileSystem, | |
| 1138 | .LOOP => return error.SymLinkLoop, | |
| 1139 | .TXTBSY => return error.FileBusy, | |
| 1140 | .NOTDIR => return error.FileNotFound, | |
| 1141 | .NOENT => return error.FileNotFound, | |
| 1142 | .NAMETOOLONG => return error.NameTooLong, | |
| 1143 | .INVAL => |err| return errnoBug(err), | |
| 1144 | .FAULT => |err| return errnoBug(err), | |
| 1145 | .IO => return error.InputOutput, | |
| 1146 | .NOMEM => return error.SystemResources, | |
| 1147 | .ILSEQ => return error.BadPathName, // TODO move to wasi | |
| 1148 | else => |err| return posix.unexpectedErrno(err), | |
| 1149 | } | |
| 1150 | } | |
| 1151 | } | |
| 1152 | ||
| 1153 | fn dirAccessWasi( | |
| 1154 | userdata: ?*anyopaque, | |
| 1155 | dir: Io.Dir, | |
| 1156 | sub_path: []const u8, | |
| 1157 | options: Io.File.OpenFlags, | |
| 1158 | ) Io.File.AccessError!void { | |
| 1159 | if (builtin.link_libc) return dirAccessPosix(userdata, dir, sub_path, options); | |
| 1160 | const t: *Threaded = @ptrCast(@alignCast(userdata)); | |
| 1161 | const wasi = std.os.wasi; | |
| 1162 | const flags: wasi.lookupflags_t = .{ | |
| 1163 | .SYMLINK_FOLLOW = @intFromBool(options.follow_symlinks), | |
| 1164 | }; | |
| 1165 | const stat = while (true) { | |
| 1166 | var stat: wasi.filestat_t = undefined; | |
| 1167 | try t.checkCancel(); | |
| 1168 | switch (wasi.path_filestat_get(dir.handle, flags, sub_path.ptr, sub_path.len, &stat)) { | |
| 1169 | .SUCCESS => break statFromWasi(stat), | |
| 1170 | .INTR => continue, | |
| 1171 | .CANCELED => return error.Canceled, | |
| 1172 | ||
| 1173 | .INVAL => |err| return errnoBug(err), | |
| 1174 | .BADF => |err| return errnoBug(err), // Always a race condition. | |
| 1175 | .NOMEM => return error.SystemResources, | |
| 1176 | .ACCES => return error.AccessDenied, | |
| 1177 | .FAULT => |err| return errnoBug(err), | |
| 1178 | .NAMETOOLONG => return error.NameTooLong, | |
| 1179 | .NOENT => return error.FileNotFound, | |
| 1180 | .NOTDIR => return error.FileNotFound, | |
| 1181 | .NOTCAPABLE => return error.AccessDenied, | |
| 1182 | .ILSEQ => return error.BadPathName, | |
| 1183 | else => |err| return posix.unexpectedErrno(err), | |
| 1184 | } | |
| 1185 | }; | |
| 1186 | ||
| 1187 | if (!options.mode.read and !options.mode.write and !options.mode.execute) | |
| 1188 | return; | |
| 1189 | ||
| 1190 | var directory: wasi.fdstat_t = undefined; | |
| 1191 | if (wasi.fd_fdstat_get(dir.handle, &directory) != .SUCCESS) | |
| 1192 | return error.AccessDenied; | |
| 1193 | ||
| 1194 | var rights: wasi.rights_t = .{}; | |
| 1195 | if (options.mode.read) { | |
| 1196 | if (stat.filetype == .DIRECTORY) { | |
| 1197 | rights.FD_READDIR = true; | |
| 1198 | } else { | |
| 1199 | rights.FD_READ = true; | |
| 1200 | } | |
| 1201 | } | |
| 1202 | if (options.mode.write) | |
| 1203 | rights.FD_WRITE = true; | |
| 1204 | ||
| 1205 | // No validation for execution. | |
| 1206 | ||
| 1207 | // https://github.com/ziglang/zig/issues/18882 | |
| 1208 | const rights_int: u64 = @bitCast(rights); | |
| 1209 | const inheriting_int: u64 = @bitCast(directory.fs_rights_inheriting); | |
| 1210 | if ((rights_int & inheriting_int) != rights_int) | |
| 1211 | return error.AccessDenied; | |
| 1212 | } | |
| 1213 | ||
| 1106 | 1214 | fn dirCreateFilePosix( |
| 1107 | 1215 | userdata: ?*anyopaque, |
| 1108 | 1216 | dir: Io.Dir, |
lib/std/fs.zig+4-3| ... | ... | @@ -1,14 +1,15 @@ |
| 1 | 1 | //! File System. |
| 2 | const builtin = @import("builtin"); | |
| 3 | const native_os = builtin.os.tag; | |
| 2 | 4 | |
| 3 | 5 | const std = @import("std.zig"); |
| 4 | const builtin = @import("builtin"); | |
| 6 | const Io = std.Io; | |
| 5 | 7 | const root = @import("root"); |
| 6 | 8 | const mem = std.mem; |
| 7 | 9 | const base64 = std.base64; |
| 8 | 10 | const crypto = std.crypto; |
| 9 | 11 | const Allocator = std.mem.Allocator; |
| 10 | 12 | const assert = std.debug.assert; |
| 11 | const native_os = builtin.os.tag; | |
| 12 | 13 | const posix = std.posix; |
| 13 | 14 | const windows = std.os.windows; |
| 14 | 15 | |
| ... | ... | @@ -274,7 +275,7 @@ pub fn openFileAbsoluteW(absolute_path_w: []const u16, flags: File.OpenFlags) Fi |
| 274 | 275 | /// On Windows, `absolute_path` should be encoded as [WTF-8](https://wtf-8.codeberg.page/). |
| 275 | 276 | /// On WASI, `absolute_path` should be encoded as valid UTF-8. |
| 276 | 277 | /// On other platforms, `absolute_path` is an opaque sequence of bytes with no particular encoding. |
| 277 | pub fn accessAbsolute(absolute_path: []const u8, flags: File.OpenFlags) Dir.AccessError!void { | |
| 278 | pub fn accessAbsolute(absolute_path: []const u8, flags: Io.Dir.AccessOptions) Dir.AccessError!void { | |
| 278 | 279 | assert(path.isAbsolute(absolute_path)); |
| 279 | 280 | try cwd().access(absolute_path, flags); |
| 280 | 281 | } |
lib/std/fs/Dir.zig+7-40| ... | ... | @@ -2353,47 +2353,14 @@ pub fn writeFile(self: Dir, options: WriteFileOptions) WriteFileError!void { |
| 2353 | 2353 | try file.writeAll(options.data); |
| 2354 | 2354 | } |
| 2355 | 2355 | |
| 2356 | pub const AccessError = posix.AccessError; | |
| 2356 | /// Deprecated in favor of `Io.Dir.AccessError`. | |
| 2357 | pub const AccessError = Io.Dir.AccessError; | |
| 2357 | 2358 | |
| 2358 | /// Test accessing `sub_path`. | |
| 2359 | /// On Windows, `sub_path` should be encoded as [WTF-8](https://wtf-8.codeberg.page/). | |
| 2360 | /// On WASI, `sub_path` should be encoded as valid UTF-8. | |
| 2361 | /// On other platforms, `sub_path` is an opaque sequence of bytes with no particular encoding. | |
| 2362 | /// Be careful of Time-Of-Check-Time-Of-Use race conditions when using this function. | |
| 2363 | /// For example, instead of testing if a file exists and then opening it, just | |
| 2364 | /// open it and handle the error for file not found. | |
| 2365 | pub fn access(self: Dir, sub_path: []const u8, flags: File.OpenFlags) AccessError!void { | |
| 2366 | if (native_os == .windows) { | |
| 2367 | const sub_path_w = try windows.sliceToPrefixedFileW(self.fd, sub_path); | |
| 2368 | return self.accessW(sub_path_w.span().ptr, flags); | |
| 2369 | } | |
| 2370 | const path_c = try posix.toPosixPath(sub_path); | |
| 2371 | return self.accessZ(&path_c, flags); | |
| 2372 | } | |
| 2373 | ||
| 2374 | /// Same as `access` except the path parameter is null-terminated. | |
| 2375 | pub fn accessZ(self: Dir, sub_path: [*:0]const u8, flags: File.OpenFlags) AccessError!void { | |
| 2376 | if (native_os == .windows) { | |
| 2377 | const sub_path_w = try windows.cStrToPrefixedFileW(self.fd, sub_path); | |
| 2378 | return self.accessW(sub_path_w.span().ptr, flags); | |
| 2379 | } | |
| 2380 | const os_mode = switch (flags.mode) { | |
| 2381 | .read_only => @as(u32, posix.F_OK), | |
| 2382 | .write_only => @as(u32, posix.W_OK), | |
| 2383 | .read_write => @as(u32, posix.R_OK | posix.W_OK), | |
| 2384 | }; | |
| 2385 | const result = posix.faccessatZ(self.fd, sub_path, os_mode, 0); | |
| 2386 | return result; | |
| 2387 | } | |
| 2388 | ||
| 2389 | /// Same as `access` except asserts the target OS is Windows and the path parameter is | |
| 2390 | /// * WTF-16 LE encoded | |
| 2391 | /// * null-terminated | |
| 2392 | /// * relative or has the NT namespace prefix | |
| 2393 | /// TODO currently this ignores `flags`. | |
| 2394 | pub fn accessW(self: Dir, sub_path_w: [*:0]const u16, flags: File.OpenFlags) AccessError!void { | |
| 2395 | _ = flags; | |
| 2396 | return posix.faccessatW(self.fd, sub_path_w); | |
| 2359 | /// Deprecated in favor of `Io.Dir.access`. | |
| 2360 | pub fn access(self: Dir, sub_path: []const u8, options: Io.Dir.AccessOptions) AccessError!void { | |
| 2361 | var threaded: Io.Threaded = .init_single_threaded; | |
| 2362 | const io = threaded.io(); | |
| 2363 | return Io.Dir.access(self.adaptToNewApi(), io, sub_path, options); | |
| 2397 | 2364 | } |
| 2398 | 2365 | |
| 2399 | 2366 | pub const CopyFileOptions = struct { |
lib/std/fs/File.zig+6-59| ... | ... | @@ -40,65 +40,12 @@ pub const default_mode = switch (builtin.os.tag) { |
| 40 | 40 | |
| 41 | 41 | /// Deprecated in favor of `Io.File.OpenError`. |
| 42 | 42 | pub const OpenError = Io.File.OpenError || error{WouldBlock}; |
| 43 | ||
| 44 | pub const OpenMode = enum { | |
| 45 | read_only, | |
| 46 | write_only, | |
| 47 | read_write, | |
| 48 | }; | |
| 49 | ||
| 50 | pub const Lock = enum { | |
| 51 | none, | |
| 52 | shared, | |
| 53 | exclusive, | |
| 54 | }; | |
| 55 | ||
| 56 | pub const OpenFlags = struct { | |
| 57 | mode: OpenMode = .read_only, | |
| 58 | ||
| 59 | /// Open the file with an advisory lock to coordinate with other processes | |
| 60 | /// accessing it at the same time. An exclusive lock will prevent other | |
| 61 | /// processes from acquiring a lock. A shared lock will prevent other | |
| 62 | /// processes from acquiring a exclusive lock, but does not prevent | |
| 63 | /// other process from getting their own shared locks. | |
| 64 | /// | |
| 65 | /// The lock is advisory, except on Linux in very specific circumstances[1]. | |
| 66 | /// This means that a process that does not respect the locking API can still get access | |
| 67 | /// to the file, despite the lock. | |
| 68 | /// | |
| 69 | /// On these operating systems, the lock is acquired atomically with | |
| 70 | /// opening the file: | |
| 71 | /// * Darwin | |
| 72 | /// * DragonFlyBSD | |
| 73 | /// * FreeBSD | |
| 74 | /// * Haiku | |
| 75 | /// * NetBSD | |
| 76 | /// * OpenBSD | |
| 77 | /// On these operating systems, the lock is acquired via a separate syscall | |
| 78 | /// after opening the file: | |
| 79 | /// * Linux | |
| 80 | /// * Windows | |
| 81 | /// | |
| 82 | /// [1]: https://www.kernel.org/doc/Documentation/filesystems/mandatory-locking.txt | |
| 83 | lock: Lock = .none, | |
| 84 | ||
| 85 | /// Sets whether or not to wait until the file is locked to return. If set to true, | |
| 86 | /// `error.WouldBlock` will be returned. Otherwise, the file will wait until the file | |
| 87 | /// is available to proceed. | |
| 88 | lock_nonblocking: bool = false, | |
| 89 | ||
| 90 | /// Set this to allow the opened file to automatically become the | |
| 91 | /// controlling TTY for the current process. | |
| 92 | allow_ctty: bool = false, | |
| 93 | ||
| 94 | pub fn isRead(self: OpenFlags) bool { | |
| 95 | return self.mode != .write_only; | |
| 96 | } | |
| 97 | ||
| 98 | pub fn isWrite(self: OpenFlags) bool { | |
| 99 | return self.mode != .read_only; | |
| 100 | } | |
| 101 | }; | |
| 43 | /// Deprecated in favor of `Io.File.OpenMode`. | |
| 44 | pub const OpenMode = Io.File.OpenMode; | |
| 45 | /// Deprecated in favor of `Io.File.Lock`. | |
| 46 | pub const Lock = Io.File.Lock; | |
| 47 | /// Deprecated in favor of `Io.File.OpenFlags`. | |
| 48 | pub const OpenFlags = Io.File.OpenFlags; | |
| 102 | 49 | |
| 103 | 50 | pub const CreateFlags = struct { |
| 104 | 51 | /// Whether the file will be created with read access. |
lib/std/posix.zig+2-118| ... | ... | @@ -4360,8 +4360,7 @@ pub const FStatAtError = FStatError || error{ |
| 4360 | 4360 | NameTooLong, |
| 4361 | 4361 | FileNotFound, |
| 4362 | 4362 | SymLinkLoop, |
| 4363 | /// WASI-only; file paths must be valid UTF-8. | |
| 4364 | InvalidUtf8, | |
| 4363 | BadPathName, | |
| 4365 | 4364 | }; |
| 4366 | 4365 | |
| 4367 | 4366 | /// Similar to `fstat`, but returns stat of a resource pointed to by `pathname` |
| ... | ... | @@ -4900,7 +4899,7 @@ pub fn access(path: []const u8, mode: u32) AccessError!void { |
| 4900 | 4899 | _ = try windows.GetFileAttributesW(path_w.span().ptr); |
| 4901 | 4900 | return; |
| 4902 | 4901 | } else if (native_os == .wasi and !builtin.link_libc) { |
| 4903 | return faccessat(AT.FDCWD, path, mode, 0); | |
| 4902 | @compileError("wasi doesn't support absolute paths"); | |
| 4904 | 4903 | } |
| 4905 | 4904 | const path_c = try toPosixPath(path); |
| 4906 | 4905 | return accessZ(&path_c, mode); |
| ... | ... | @@ -4934,121 +4933,6 @@ pub fn accessZ(path: [*:0]const u8, mode: u32) AccessError!void { |
| 4934 | 4933 | } |
| 4935 | 4934 | } |
| 4936 | 4935 | |
| 4937 | /// Check user's permissions for a file, based on an open directory handle. | |
| 4938 | /// | |
| 4939 | /// * On Windows, asserts `path` is valid [WTF-8](https://wtf-8.codeberg.page/). | |
| 4940 | /// * On WASI, invalid UTF-8 passed to `path` causes `error.InvalidUtf8`. | |
| 4941 | /// * On other platforms, `path` is an opaque sequence of bytes with no particular encoding. | |
| 4942 | /// | |
| 4943 | /// On Windows, `mode` is ignored. This is a POSIX API that is only partially supported by | |
| 4944 | /// Windows. See `fs` for the cross-platform file system API. | |
| 4945 | pub fn faccessat(dirfd: fd_t, path: []const u8, mode: u32, flags: u32) AccessError!void { | |
| 4946 | if (native_os == .windows) { | |
| 4947 | const path_w = try windows.sliceToPrefixedFileW(dirfd, path); | |
| 4948 | return faccessatW(dirfd, path_w.span().ptr); | |
| 4949 | } else if (native_os == .wasi and !builtin.link_libc) { | |
| 4950 | const resolved: RelativePathWasi = .{ .dir_fd = dirfd, .relative_path = path }; | |
| 4951 | ||
| 4952 | const st = try std.os.fstatat_wasi(dirfd, path, .{ | |
| 4953 | .SYMLINK_FOLLOW = (flags & AT.SYMLINK_NOFOLLOW) == 0, | |
| 4954 | }); | |
| 4955 | ||
| 4956 | if (mode != F_OK) { | |
| 4957 | var directory: wasi.fdstat_t = undefined; | |
| 4958 | if (wasi.fd_fdstat_get(resolved.dir_fd, &directory) != .SUCCESS) { | |
| 4959 | return error.AccessDenied; | |
| 4960 | } | |
| 4961 | ||
| 4962 | var rights: wasi.rights_t = .{}; | |
| 4963 | if (mode & R_OK != 0) { | |
| 4964 | if (st.filetype == .DIRECTORY) { | |
| 4965 | rights.FD_READDIR = true; | |
| 4966 | } else { | |
| 4967 | rights.FD_READ = true; | |
| 4968 | } | |
| 4969 | } | |
| 4970 | if (mode & W_OK != 0) { | |
| 4971 | rights.FD_WRITE = true; | |
| 4972 | } | |
| 4973 | // No validation for X_OK | |
| 4974 | ||
| 4975 | // https://github.com/ziglang/zig/issues/18882 | |
| 4976 | const rights_int: u64 = @bitCast(rights); | |
| 4977 | const inheriting_int: u64 = @bitCast(directory.fs_rights_inheriting); | |
| 4978 | if ((rights_int & inheriting_int) != rights_int) { | |
| 4979 | return error.AccessDenied; | |
| 4980 | } | |
| 4981 | } | |
| 4982 | return; | |
| 4983 | } | |
| 4984 | const path_c = try toPosixPath(path); | |
| 4985 | return faccessatZ(dirfd, &path_c, mode, flags); | |
| 4986 | } | |
| 4987 | ||
| 4988 | /// Same as `faccessat` except the path parameter is null-terminated. | |
| 4989 | pub fn faccessatZ(dirfd: fd_t, path: [*:0]const u8, mode: u32, flags: u32) AccessError!void { | |
| 4990 | if (native_os == .windows) { | |
| 4991 | const path_w = try windows.cStrToPrefixedFileW(dirfd, path); | |
| 4992 | return faccessatW(dirfd, path_w.span().ptr); | |
| 4993 | } else if (native_os == .wasi and !builtin.link_libc) { | |
| 4994 | return faccessat(dirfd, mem.sliceTo(path, 0), mode, flags); | |
| 4995 | } | |
| 4996 | switch (errno(system.faccessat(dirfd, path, mode, flags))) { | |
| 4997 | .SUCCESS => return, | |
| 4998 | .ACCES => return error.AccessDenied, | |
| 4999 | .PERM => return error.PermissionDenied, | |
| 5000 | .ROFS => return error.ReadOnlyFileSystem, | |
| 5001 | .LOOP => return error.SymLinkLoop, | |
| 5002 | .TXTBSY => return error.FileBusy, | |
| 5003 | .NOTDIR => return error.FileNotFound, | |
| 5004 | .NOENT => return error.FileNotFound, | |
| 5005 | .NAMETOOLONG => return error.NameTooLong, | |
| 5006 | .INVAL => unreachable, | |
| 5007 | .FAULT => unreachable, | |
| 5008 | .IO => return error.InputOutput, | |
| 5009 | .NOMEM => return error.SystemResources, | |
| 5010 | .ILSEQ => return error.BadPathName, | |
| 5011 | else => |err| return unexpectedErrno(err), | |
| 5012 | } | |
| 5013 | } | |
| 5014 | ||
| 5015 | /// Same as `faccessat` except asserts the target is Windows and the path parameter | |
| 5016 | /// is NtDll-prefixed, null-terminated, WTF-16 encoded. | |
| 5017 | pub fn faccessatW(dirfd: fd_t, sub_path_w: [*:0]const u16) AccessError!void { | |
| 5018 | if (sub_path_w[0] == '.' and sub_path_w[1] == 0) { | |
| 5019 | return; | |
| 5020 | } | |
| 5021 | if (sub_path_w[0] == '.' and sub_path_w[1] == '.' and sub_path_w[2] == 0) { | |
| 5022 | return; | |
| 5023 | } | |
| 5024 | ||
| 5025 | const path_len_bytes = cast(u16, mem.sliceTo(sub_path_w, 0).len * 2) orelse return error.NameTooLong; | |
| 5026 | var nt_name = windows.UNICODE_STRING{ | |
| 5027 | .Length = path_len_bytes, | |
| 5028 | .MaximumLength = path_len_bytes, | |
| 5029 | .Buffer = @constCast(sub_path_w), | |
| 5030 | }; | |
| 5031 | var attr = windows.OBJECT_ATTRIBUTES{ | |
| 5032 | .Length = @sizeOf(windows.OBJECT_ATTRIBUTES), | |
| 5033 | .RootDirectory = if (fs.path.isAbsoluteWindowsW(sub_path_w)) null else dirfd, | |
| 5034 | .Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here. | |
| 5035 | .ObjectName = &nt_name, | |
| 5036 | .SecurityDescriptor = null, | |
| 5037 | .SecurityQualityOfService = null, | |
| 5038 | }; | |
| 5039 | var basic_info: windows.FILE_BASIC_INFORMATION = undefined; | |
| 5040 | switch (windows.ntdll.NtQueryAttributesFile(&attr, &basic_info)) { | |
| 5041 | .SUCCESS => return, | |
| 5042 | .OBJECT_NAME_NOT_FOUND => return error.FileNotFound, | |
| 5043 | .OBJECT_PATH_NOT_FOUND => return error.FileNotFound, | |
| 5044 | .OBJECT_NAME_INVALID => unreachable, | |
| 5045 | .INVALID_PARAMETER => unreachable, | |
| 5046 | .ACCESS_DENIED => return error.AccessDenied, | |
| 5047 | .OBJECT_PATH_SYNTAX_BAD => unreachable, | |
| 5048 | else => |rc| return windows.unexpectedStatus(rc), | |
| 5049 | } | |
| 5050 | } | |
| 5051 | ||
| 5052 | 4936 | pub const PipeError = error{ |
| 5053 | 4937 | SystemFdQuotaExceeded, |
| 5054 | 4938 | ProcessFdQuotaExceeded, |