authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-03-27 12:24:09+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-03-27 12:24:09+01:00
log2d3694ab42b73ba09124d5f1ddbfd9adaea79c47
tree8c0adc21784e13f0c7a5ce9d88167b84c8c62b2c
parent1f79a9e4e300affd111b51ec172bbeff42ca755a
parent2e4482cd14d6af958c2e16d7db4d6cf73bd043b8

Merge pull request 'std.Io.Dir: add resolve_beneath flag and implement for freebsd,macos' (#31684) from resolve_beneath into master

Reviewed-on: https://codeberg.org/ziglang/zig/pulls/31684

5 files changed, 181 insertions(+), 175 deletions(-)

lib/std/Io.zig+3-3
...@@ -160,9 +160,9 @@ pub const VTable = struct {...@@ -160,9 +160,9 @@ pub const VTable = struct {
160 dirStat: *const fn (?*anyopaque, Dir) Dir.StatError!Dir.Stat,160 dirStat: *const fn (?*anyopaque, Dir) Dir.StatError!Dir.Stat,
161 dirStatFile: *const fn (?*anyopaque, Dir, []const u8, Dir.StatFileOptions) Dir.StatFileError!File.Stat,161 dirStatFile: *const fn (?*anyopaque, Dir, []const u8, Dir.StatFileOptions) Dir.StatFileError!File.Stat,
162 dirAccess: *const fn (?*anyopaque, Dir, []const u8, Dir.AccessOptions) Dir.AccessError!void,162 dirAccess: *const fn (?*anyopaque, Dir, []const u8, Dir.AccessOptions) Dir.AccessError!void,
163 dirCreateFile: *const fn (?*anyopaque, Dir, []const u8, File.CreateFlags) File.OpenError!File,163 dirCreateFile: *const fn (?*anyopaque, Dir, []const u8, Dir.CreateFileOptions) File.OpenError!File,
164 dirCreateFileAtomic: *const fn (?*anyopaque, Dir, []const u8, Dir.CreateFileAtomicOptions) Dir.CreateFileAtomicError!File.Atomic,164 dirCreateFileAtomic: *const fn (?*anyopaque, Dir, []const u8, Dir.CreateFileAtomicOptions) Dir.CreateFileAtomicError!File.Atomic,
165 dirOpenFile: *const fn (?*anyopaque, Dir, []const u8, File.OpenFlags) File.OpenError!File,165 dirOpenFile: *const fn (?*anyopaque, Dir, []const u8, Dir.OpenFileOptions) File.OpenError!File,
166 dirClose: *const fn (?*anyopaque, []const Dir) void,166 dirClose: *const fn (?*anyopaque, []const Dir) void,
167 dirRead: *const fn (?*anyopaque, *Dir.Reader, []Dir.Entry) Dir.Reader.Error!usize,167 dirRead: *const fn (?*anyopaque, *Dir.Reader, []Dir.Entry) Dir.Reader.Error!usize,
168 dirRealPath: *const fn (?*anyopaque, Dir, out_buffer: []u8) Dir.RealPathError!usize,168 dirRealPath: *const fn (?*anyopaque, Dir, out_buffer: []u8) Dir.RealPathError!usize,
...@@ -211,7 +211,7 @@ pub const VTable = struct {...@@ -211,7 +211,7 @@ pub const VTable = struct {
211 fileMemoryMapRead: *const fn (?*anyopaque, *File.MemoryMap) File.ReadPositionalError!void,211 fileMemoryMapRead: *const fn (?*anyopaque, *File.MemoryMap) File.ReadPositionalError!void,
212 fileMemoryMapWrite: *const fn (?*anyopaque, *File.MemoryMap) File.WritePositionalError!void,212 fileMemoryMapWrite: *const fn (?*anyopaque, *File.MemoryMap) File.WritePositionalError!void,
213213
214 processExecutableOpen: *const fn (?*anyopaque, File.OpenFlags) std.process.OpenExecutableError!File,214 processExecutableOpen: *const fn (?*anyopaque, Dir.OpenFileOptions) std.process.OpenExecutableError!File,
215 processExecutablePath: *const fn (?*anyopaque, buffer: []u8) std.process.ExecutablePathError!usize,215 processExecutablePath: *const fn (?*anyopaque, buffer: []u8) std.process.ExecutablePathError!usize,
216 lockStderr: *const fn (?*anyopaque, ?Terminal.Mode) Cancelable!LockedStderr,216 lockStderr: *const fn (?*anyopaque, ?Terminal.Mode) Cancelable!LockedStderr,
217 tryLockStderr: *const fn (?*anyopaque, ?Terminal.Mode) Cancelable!?LockedStderr,217 tryLockStderr: *const fn (?*anyopaque, ?Terminal.Mode) Cancelable!?LockedStderr,
lib/std/Io/Dir.zig+122-7
...@@ -495,6 +495,76 @@ pub fn closeMany(io: Io, dirs: []const Dir) void {...@@ -495,6 +495,76 @@ pub fn closeMany(io: Io, dirs: []const Dir) void {
495 return io.vtable.dirClose(io.userdata, dirs);495 return io.vtable.dirClose(io.userdata, dirs);
496}496}
497497
498pub const OpenFileOptions = struct {
499 mode: Mode = .read_only,
500 /// Determines the behavior when opening a path that refers to a directory.
501 ///
502 /// If set to true, directories may be opened, but `error.IsDir` is still
503 /// possible in certain scenarios, e.g. attempting to open a directory with
504 /// write permissions.
505 ///
506 /// If set to false, `error.IsDir` will always be returned when opening a directory.
507 ///
508 /// When set to false:
509 /// * On Windows, the behavior is implemented without any extra syscalls.
510 /// * On other operating systems, the behavior is implemented with an additional
511 /// `fstat` syscall.
512 allow_directory: bool = true,
513 /// Indicates intent for only some operations to be performed on this
514 /// opened file:
515 /// * `close`
516 /// * `stat`
517 /// On Linux and FreeBSD, this corresponds to `std.posix.O.PATH`.
518 path_only: bool = false,
519 /// Open the file with an advisory lock to coordinate with other processes
520 /// accessing it at the same time. An exclusive lock will prevent other
521 /// processes from acquiring a lock. A shared lock will prevent other
522 /// processes from acquiring a exclusive lock, but does not prevent
523 /// other process from getting their own shared locks.
524 ///
525 /// The lock is advisory, except on Linux in very specific circumstances[1].
526 /// This means that a process that does not respect the locking API can still get access
527 /// to the file, despite the lock.
528 ///
529 /// On these operating systems, the lock is acquired atomically with
530 /// opening the file:
531 /// * Darwin
532 /// * DragonFlyBSD
533 /// * FreeBSD
534 /// * Haiku
535 /// * NetBSD
536 /// * OpenBSD
537 /// On these operating systems, the lock is acquired via a separate syscall
538 /// after opening the file:
539 /// * Linux
540 /// * Windows
541 ///
542 /// [1]: https://www.kernel.org/doc/Documentation/filesystems/mandatory-locking.txt
543 lock: File.Lock = .none,
544 /// Sets whether or not to wait until the file is locked to return. If set to true,
545 /// `error.WouldBlock` will be returned. Otherwise, the file will wait until the file
546 /// is available to proceed.
547 lock_nonblocking: bool = false,
548 /// Set this to allow the opened file to automatically become the
549 /// controlling TTY for the current process.
550 allow_ctty: bool = false,
551 follow_symlinks: bool = true,
552 /// If supported by the operating system, attempted path resolution that
553 /// would escape the directory instead returns `error.AccessDenied`. If
554 /// unsupported, this option is ignored.
555 resolve_beneath: bool = false,
556
557 pub const Mode = enum { read_only, write_only, read_write };
558
559 pub fn isRead(self: OpenFileOptions) bool {
560 return self.mode != .write_only;
561 }
562
563 pub fn isWrite(self: OpenFileOptions) bool {
564 return self.mode != .read_only;
565 }
566};
567
498/// Opens a file for reading or writing, without attempting to create a new file.568/// Opens a file for reading or writing, without attempting to create a new file.
499///569///
500/// To create a new file, see `createFile`.570/// To create a new file, see `createFile`.
...@@ -504,15 +574,60 @@ pub fn closeMany(io: Io, dirs: []const Dir) void {...@@ -504,15 +574,60 @@ pub fn closeMany(io: Io, dirs: []const Dir) void {
504/// On Windows, `sub_path` should be encoded as [WTF-8](https://wtf-8.codeberg.page/).574/// On Windows, `sub_path` should be encoded as [WTF-8](https://wtf-8.codeberg.page/).
505/// On WASI, `sub_path` should be encoded as valid UTF-8.575/// On WASI, `sub_path` should be encoded as valid UTF-8.
506/// On other platforms, `sub_path` is an opaque sequence of bytes with no particular encoding.576/// On other platforms, `sub_path` is an opaque sequence of bytes with no particular encoding.
507pub fn openFile(dir: Dir, io: Io, sub_path: []const u8, flags: File.OpenFlags) File.OpenError!File {577pub fn openFile(dir: Dir, io: Io, sub_path: []const u8, options: OpenFileOptions) File.OpenError!File {
508 return io.vtable.dirOpenFile(io.userdata, dir, sub_path, flags);578 return io.vtable.dirOpenFile(io.userdata, dir, sub_path, options);
509}579}
510580
511pub fn openFileAbsolute(io: Io, absolute_path: []const u8, flags: File.OpenFlags) File.OpenError!File {581pub fn openFileAbsolute(io: Io, absolute_path: []const u8, options: OpenFileOptions) File.OpenError!File {
512 assert(path.isAbsolute(absolute_path));582 assert(path.isAbsolute(absolute_path));
513 return openFile(.cwd(), io, absolute_path, flags);583 return openFile(.cwd(), io, absolute_path, options);
514}584}
515585
586pub const CreateFileOptions = struct {
587 /// Whether the file will be created with read access.
588 read: bool = false,
589 /// If the file already exists, and is a regular file, and the access
590 /// mode allows writing, it will be truncated to length 0.
591 truncate: bool = true,
592 /// Ensures that this open call creates the file, otherwise causes
593 /// `error.PathAlreadyExists` to be returned.
594 exclusive: bool = false,
595 /// Open the file with an advisory lock to coordinate with other processes
596 /// accessing it at the same time. An exclusive lock will prevent other
597 /// processes from acquiring a lock. A shared lock will prevent other
598 /// processes from acquiring a exclusive lock, but does not prevent
599 /// other process from getting their own shared locks.
600 ///
601 /// The lock is advisory, except on Linux in very specific circumstances[1].
602 /// This means that a process that does not respect the locking API can still get access
603 /// to the file, despite the lock.
604 ///
605 /// On these operating systems, the lock is acquired atomically with
606 /// opening the file:
607 /// * Darwin
608 /// * DragonFlyBSD
609 /// * FreeBSD
610 /// * Haiku
611 /// * NetBSD
612 /// * OpenBSD
613 /// On these operating systems, the lock is acquired via a separate syscall
614 /// after opening the file:
615 /// * Linux
616 /// * Windows
617 ///
618 /// [1]: https://www.kernel.org/doc/Documentation/filesystems/mandatory-locking.txt
619 lock: File.Lock = .none,
620 /// Sets whether or not to wait until the file is locked to return. If set to true,
621 /// `error.WouldBlock` will be returned. Otherwise, the file will wait until the file
622 /// is available to proceed.
623 lock_nonblocking: bool = false,
624 permissions: Permissions = .default_file,
625 /// If supported by the operating system, attempted path resolution that
626 /// would escape the directory instead returns `error.AccessDenied`. If
627 /// unsupported, this option is ignored.
628 resolve_beneath: bool = false,
629};
630
516/// Creates, opens, or overwrites a file with write access.631/// Creates, opens, or overwrites a file with write access.
517///632///
518/// Allocates a resource to be dellocated with `File.close`.633/// Allocates a resource to be dellocated with `File.close`.
...@@ -520,11 +635,11 @@ pub fn openFileAbsolute(io: Io, absolute_path: []const u8, flags: File.OpenFlags...@@ -520,11 +635,11 @@ pub fn openFileAbsolute(io: Io, absolute_path: []const u8, flags: File.OpenFlags
520/// On Windows, `sub_path` should be encoded as [WTF-8](https://wtf-8.codeberg.page/).635/// On Windows, `sub_path` should be encoded as [WTF-8](https://wtf-8.codeberg.page/).
521/// On WASI, `sub_path` should be encoded as valid UTF-8.636/// On WASI, `sub_path` should be encoded as valid UTF-8.
522/// On other platforms, `sub_path` is an opaque sequence of bytes with no particular encoding.637/// On other platforms, `sub_path` is an opaque sequence of bytes with no particular encoding.
523pub fn createFile(dir: Dir, io: Io, sub_path: []const u8, flags: File.CreateFlags) File.OpenError!File {638pub fn createFile(dir: Dir, io: Io, sub_path: []const u8, flags: CreateFileOptions) File.OpenError!File {
524 return io.vtable.dirCreateFile(io.userdata, dir, sub_path, flags);639 return io.vtable.dirCreateFile(io.userdata, dir, sub_path, flags);
525}640}
526641
527pub fn createFileAbsolute(io: Io, absolute_path: []const u8, flags: File.CreateFlags) File.OpenError!File {642pub fn createFileAbsolute(io: Io, absolute_path: []const u8, flags: CreateFileOptions) File.OpenError!File {
528 return createFile(.cwd(), io, absolute_path, flags);643 return createFile(.cwd(), io, absolute_path, flags);
529}644}
530645
...@@ -534,7 +649,7 @@ pub const WriteFileOptions = struct {...@@ -534,7 +649,7 @@ pub const WriteFileOptions = struct {
534 /// On other platforms, `sub_path` is an opaque sequence of bytes with no particular encoding.649 /// On other platforms, `sub_path` is an opaque sequence of bytes with no particular encoding.
535 sub_path: []const u8,650 sub_path: []const u8,
536 data: []const u8,651 data: []const u8,
537 flags: File.CreateFlags = .{},652 flags: CreateFileOptions = .{},
538};653};
539654
540pub const WriteFileError = File.Writer.Error || File.OpenError;655pub const WriteFileError = File.Writer.Error || File.OpenError;
lib/std/Io/File.zig+6-118
...@@ -142,11 +142,8 @@ pub fn stat(file: File, io: Io) StatError!Stat {...@@ -142,11 +142,8 @@ pub fn stat(file: File, io: Io) StatError!Stat {
142 return io.vtable.fileStat(io.userdata, file);142 return io.vtable.fileStat(io.userdata, file);
143}143}
144144
145pub const OpenMode = enum {145/// Deprecated, renamed to `Dir.OpenFileOptions.Mode`.
146 read_only,146pub const OpenMode = Dir.OpenFileOptions.Mode;
147 write_only,
148 read_write,
149};
150147
151pub const Lock = enum {148pub const Lock = enum {
152 none,149 none,
...@@ -154,120 +151,11 @@ pub const Lock = enum {...@@ -154,120 +151,11 @@ pub const Lock = enum {
154 exclusive,151 exclusive,
155};152};
156153
157pub const OpenFlags = struct {154/// Deprecated, renamed to `Dir.OpenFileOptions`
158 mode: OpenMode = .read_only,155pub const OpenFlags = Dir.OpenFileOptions;
159
160 /// Determines the behavior when opening a path that refers to a directory.
161 ///
162 /// If set to true, directories may be opened, but `error.IsDir` is still
163 /// possible in certain scenarios, e.g. attempting to open a directory with
164 /// write permissions.
165 ///
166 /// If set to false, `error.IsDir` will always be returned when opening a directory.
167 ///
168 /// When set to false:
169 /// * On Windows, the behavior is implemented without any extra syscalls.
170 /// * On other operating systems, the behavior is implemented with an additional
171 /// `fstat` syscall.
172 allow_directory: bool = true,
173 /// Indicates intent for only some operations to be performed on this
174 /// opened file:
175 /// * `close`
176 /// * `stat`
177 /// On Linux and FreeBSD, this corresponds to `std.posix.O.PATH`.
178 path_only: bool = false,
179
180 /// Open the file with an advisory lock to coordinate with other processes
181 /// accessing it at the same time. An exclusive lock will prevent other
182 /// processes from acquiring a lock. A shared lock will prevent other
183 /// processes from acquiring a exclusive lock, but does not prevent
184 /// other process from getting their own shared locks.
185 ///
186 /// The lock is advisory, except on Linux in very specific circumstances[1].
187 /// This means that a process that does not respect the locking API can still get access
188 /// to the file, despite the lock.
189 ///
190 /// On these operating systems, the lock is acquired atomically with
191 /// opening the file:
192 /// * Darwin
193 /// * DragonFlyBSD
194 /// * FreeBSD
195 /// * Haiku
196 /// * NetBSD
197 /// * OpenBSD
198 /// On these operating systems, the lock is acquired via a separate syscall
199 /// after opening the file:
200 /// * Linux
201 /// * Windows
202 ///
203 /// [1]: https://www.kernel.org/doc/Documentation/filesystems/mandatory-locking.txt
204 lock: Lock = .none,
205
206 /// Sets whether or not to wait until the file is locked to return. If set to true,
207 /// `error.WouldBlock` will be returned. Otherwise, the file will wait until the file
208 /// is available to proceed.
209 lock_nonblocking: bool = false,
210
211 /// Set this to allow the opened file to automatically become the
212 /// controlling TTY for the current process.
213 allow_ctty: bool = false,
214
215 follow_symlinks: bool = true,
216
217 pub fn isRead(self: OpenFlags) bool {
218 return self.mode != .write_only;
219 }
220156
221 pub fn isWrite(self: OpenFlags) bool {157/// Deprecated, renamed to `Dir.CreateFileOptions`.
222 return self.mode != .read_only;158pub const CreateFlags = Dir.CreateFileOptions;
223 }
224};
225
226pub const CreateFlags = struct {
227 /// Whether the file will be created with read access.
228 read: bool = false,
229
230 /// If the file already exists, and is a regular file, and the access
231 /// mode allows writing, it will be truncated to length 0.
232 truncate: bool = true,
233
234 /// Ensures that this open call creates the file, otherwise causes
235 /// `error.PathAlreadyExists` to be returned.
236 exclusive: bool = false,
237
238 /// Open the file with an advisory lock to coordinate with other processes
239 /// accessing it at the same time. An exclusive lock will prevent other
240 /// processes from acquiring a lock. A shared lock will prevent other
241 /// processes from acquiring a exclusive lock, but does not prevent
242 /// other process from getting their own shared locks.
243 ///
244 /// The lock is advisory, except on Linux in very specific circumstances[1].
245 /// This means that a process that does not respect the locking API can still get access
246 /// to the file, despite the lock.
247 ///
248 /// On these operating systems, the lock is acquired atomically with
249 /// opening the file:
250 /// * Darwin
251 /// * DragonFlyBSD
252 /// * FreeBSD
253 /// * Haiku
254 /// * NetBSD
255 /// * OpenBSD
256 /// On these operating systems, the lock is acquired via a separate syscall
257 /// after opening the file:
258 /// * Linux
259 /// * Windows
260 ///
261 /// [1]: https://www.kernel.org/doc/Documentation/filesystems/mandatory-locking.txt
262 lock: Lock = .none,
263
264 /// Sets whether or not to wait until the file is locked to return. If set to true,
265 /// `error.WouldBlock` will be returned. Otherwise, the file will wait until the file
266 /// is available to proceed.
267 lock_nonblocking: bool = false,
268
269 permissions: Permissions = .default_file,
270};
271159
272pub const OpenError = error{160pub const OpenError = error{
273 PipeBusy,161 PipeBusy,
lib/std/Io/Threaded.zig+48-46
...@@ -4217,7 +4217,7 @@ fn dirCreateFilePosix(...@@ -4217,7 +4217,7 @@ fn dirCreateFilePosix(
4217 userdata: ?*anyopaque,4217 userdata: ?*anyopaque,
4218 dir: Dir,4218 dir: Dir,
4219 sub_path: []const u8,4219 sub_path: []const u8,
4220 flags: File.CreateFlags,4220 options: Dir.CreateFileOptions,
4221) File.OpenError!File {4221) File.OpenError!File {
4222 const t: *Threaded = @ptrCast(@alignCast(userdata));4222 const t: *Threaded = @ptrCast(@alignCast(userdata));
4223 _ = t;4223 _ = t;
...@@ -4225,34 +4225,35 @@ fn dirCreateFilePosix(...@@ -4225,34 +4225,35 @@ fn dirCreateFilePosix(
4225 var path_buffer: [posix.PATH_MAX]u8 = undefined;4225 var path_buffer: [posix.PATH_MAX]u8 = undefined;
4226 const sub_path_posix = try pathToPosix(sub_path, &path_buffer);4226 const sub_path_posix = try pathToPosix(sub_path, &path_buffer);
42274227
4228 var os_flags: posix.O = .{4228 var flags: posix.O = .{
4229 .ACCMODE = if (flags.read) .RDWR else .WRONLY,4229 .ACCMODE = if (options.read) .RDWR else .WRONLY,
4230 .CREAT = true,4230 .CREAT = true,
4231 .TRUNC = flags.truncate,4231 .TRUNC = options.truncate,
4232 .EXCL = flags.exclusive,4232 .EXCL = options.exclusive,
4233 };4233 };
4234 if (@hasField(posix.O, "LARGEFILE")) os_flags.LARGEFILE = true;4234 if (@hasField(posix.O, "LARGEFILE")) flags.LARGEFILE = true;
4235 if (@hasField(posix.O, "CLOEXEC")) os_flags.CLOEXEC = true;4235 if (@hasField(posix.O, "CLOEXEC")) flags.CLOEXEC = true;
4236 if (@hasField(posix.O, "RESOLVE_BENEATH")) flags.RESOLVE_BENEATH = options.resolve_beneath;
42364237
4237 // Use the O locking flags if the os supports them to acquire the lock4238 // Use the O locking flags if the os supports them to acquire the lock
4238 // atomically. Note that the NONBLOCK flag is removed after the openat()4239 // atomically. Note that the NONBLOCK flag is removed after the openat()
4239 // call is successful.4240 // call is successful.
4240 if (have_flock_open_flags) switch (flags.lock) {4241 if (have_flock_open_flags) switch (options.lock) {
4241 .none => {},4242 .none => {},
4242 .shared => {4243 .shared => {
4243 os_flags.SHLOCK = true;4244 flags.SHLOCK = true;
4244 os_flags.NONBLOCK = flags.lock_nonblocking;4245 flags.NONBLOCK = options.lock_nonblocking;
4245 },4246 },
4246 .exclusive => {4247 .exclusive => {
4247 os_flags.EXLOCK = true;4248 flags.EXLOCK = true;
4248 os_flags.NONBLOCK = flags.lock_nonblocking;4249 flags.NONBLOCK = options.lock_nonblocking;
4249 },4250 },
4250 };4251 };
42514252
4252 const fd: posix.fd_t = fd: {4253 const fd: posix.fd_t = fd: {
4253 const syscall: Syscall = try .start();4254 const syscall: Syscall = try .start();
4254 while (true) {4255 while (true) {
4255 const rc = openat_sym(dir.handle, sub_path_posix, os_flags, flags.permissions.toMode());4256 const rc = openat_sym(dir.handle, sub_path_posix, flags, options.permissions.toMode());
4256 switch (posix.errno(rc)) {4257 switch (posix.errno(rc)) {
4257 .SUCCESS => {4258 .SUCCESS => {
4258 syscall.finish();4259 syscall.finish();
...@@ -4298,9 +4299,9 @@ fn dirCreateFilePosix(...@@ -4298,9 +4299,9 @@ fn dirCreateFilePosix(
4298 };4299 };
4299 errdefer closeFd(fd);4300 errdefer closeFd(fd);
43004301
4301 if (have_flock and !have_flock_open_flags and flags.lock != .none) {4302 if (have_flock and !have_flock_open_flags and options.lock != .none) {
4302 const lock_nonblocking: i32 = if (flags.lock_nonblocking) posix.LOCK.NB else 0;4303 const lock_nonblocking: i32 = if (options.lock_nonblocking) posix.LOCK.NB else 0;
4303 const lock_flags = switch (flags.lock) {4304 const lock_flags = switch (options.lock) {
4304 .none => unreachable,4305 .none => unreachable,
4305 .shared => posix.LOCK.SH | lock_nonblocking,4306 .shared => posix.LOCK.SH | lock_nonblocking,
4306 .exclusive => posix.LOCK.EX | lock_nonblocking,4307 .exclusive => posix.LOCK.EX | lock_nonblocking,
...@@ -4332,7 +4333,7 @@ fn dirCreateFilePosix(...@@ -4332,7 +4333,7 @@ fn dirCreateFilePosix(
4332 }4333 }
4333 }4334 }
43344335
4335 if (have_flock_open_flags and flags.lock_nonblocking) {4336 if (have_flock_open_flags and options.lock_nonblocking) {
4336 var fl_flags: usize = fl: {4337 var fl_flags: usize = fl: {
4337 const syscall: Syscall = try .start();4338 const syscall: Syscall = try .start();
4338 while (true) {4339 while (true) {
...@@ -4385,7 +4386,7 @@ fn dirCreateFileWindows(...@@ -4385,7 +4386,7 @@ fn dirCreateFileWindows(
4385 userdata: ?*anyopaque,4386 userdata: ?*anyopaque,
4386 dir: Dir,4387 dir: Dir,
4387 sub_path: []const u8,4388 sub_path: []const u8,
4388 flags: File.CreateFlags,4389 flags: Dir.CreateFileOptions,
4389) File.OpenError!File {4390) File.OpenError!File {
4390 const t: *Threaded = @ptrCast(@alignCast(userdata));4391 const t: *Threaded = @ptrCast(@alignCast(userdata));
4391 _ = t;4392 _ = t;
...@@ -4535,7 +4536,7 @@ fn dirCreateFileWasi(...@@ -4535,7 +4536,7 @@ fn dirCreateFileWasi(
4535 userdata: ?*anyopaque,4536 userdata: ?*anyopaque,
4536 dir: Dir,4537 dir: Dir,
4537 sub_path: []const u8,4538 sub_path: []const u8,
4538 flags: File.CreateFlags,4539 flags: Dir.CreateFileOptions,
4539) File.OpenError!File {4540) File.OpenError!File {
4540 const t: *Threaded = @ptrCast(@alignCast(userdata));4541 const t: *Threaded = @ptrCast(@alignCast(userdata));
4541 _ = t;4542 _ = t;
...@@ -4785,45 +4786,46 @@ fn dirOpenFilePosix(...@@ -4785,45 +4786,46 @@ fn dirOpenFilePosix(
4785 userdata: ?*anyopaque,4786 userdata: ?*anyopaque,
4786 dir: Dir,4787 dir: Dir,
4787 sub_path: []const u8,4788 sub_path: []const u8,
4788 flags: File.OpenFlags,4789 options: Dir.OpenFileOptions,
4789) File.OpenError!File {4790) File.OpenError!File {
4790 const t: *Threaded = @ptrCast(@alignCast(userdata));4791 const t: *Threaded = @ptrCast(@alignCast(userdata));
47914792
4792 var path_buffer: [posix.PATH_MAX]u8 = undefined;4793 var path_buffer: [posix.PATH_MAX]u8 = undefined;
4793 const sub_path_posix = try pathToPosix(sub_path, &path_buffer);4794 const sub_path_posix = try pathToPosix(sub_path, &path_buffer);
47944795
4795 var os_flags: posix.O = switch (native_os) {4796 var flags: posix.O = switch (native_os) {
4796 .wasi => .{4797 .wasi => .{
4797 .read = flags.mode != .write_only,4798 .read = options.mode != .write_only,
4798 .write = flags.mode != .read_only,4799 .write = options.mode != .read_only,
4799 .NOFOLLOW = !flags.follow_symlinks,4800 .NOFOLLOW = !options.follow_symlinks,
4800 },4801 },
4801 else => .{4802 else => .{
4802 .ACCMODE = switch (flags.mode) {4803 .ACCMODE = switch (options.mode) {
4803 .read_only => .RDONLY,4804 .read_only => .RDONLY,
4804 .write_only => .WRONLY,4805 .write_only => .WRONLY,
4805 .read_write => .RDWR,4806 .read_write => .RDWR,
4806 },4807 },
4807 .NOFOLLOW = !flags.follow_symlinks,4808 .NOFOLLOW = !options.follow_symlinks,
4808 },4809 },
4809 };4810 };
4810 if (@hasField(posix.O, "CLOEXEC")) os_flags.CLOEXEC = true;4811 if (@hasField(posix.O, "CLOEXEC")) flags.CLOEXEC = true;
4811 if (@hasField(posix.O, "LARGEFILE")) os_flags.LARGEFILE = true;4812 if (@hasField(posix.O, "LARGEFILE")) flags.LARGEFILE = true;
4812 if (@hasField(posix.O, "NOCTTY")) os_flags.NOCTTY = !flags.allow_ctty;4813 if (@hasField(posix.O, "NOCTTY")) flags.NOCTTY = !options.allow_ctty;
4813 if (@hasField(posix.O, "PATH") and flags.path_only) os_flags.PATH = true;4814 if (@hasField(posix.O, "PATH")) flags.PATH = options.path_only;
4815 if (@hasField(posix.O, "RESOLVE_BENEATH")) flags.RESOLVE_BENEATH = options.resolve_beneath;
48144816
4815 // Use the O locking flags if the os supports them to acquire the lock4817 // Use the O locking options if the os supports them to acquire the lock
4816 // atomically. Note that the NONBLOCK flag is removed after the openat()4818 // atomically. Note that the NONBLOCK flag is removed after the openat()
4817 // call is successful.4819 // call is successful.
4818 if (have_flock_open_flags) switch (flags.lock) {4820 if (have_flock_open_flags) switch (options.lock) {
4819 .none => {},4821 .none => {},
4820 .shared => {4822 .shared => {
4821 os_flags.SHLOCK = true;4823 flags.SHLOCK = true;
4822 os_flags.NONBLOCK = flags.lock_nonblocking;4824 flags.NONBLOCK = options.lock_nonblocking;
4823 },4825 },
4824 .exclusive => {4826 .exclusive => {
4825 os_flags.EXLOCK = true;4827 flags.EXLOCK = true;
4826 os_flags.NONBLOCK = flags.lock_nonblocking;4828 flags.NONBLOCK = options.lock_nonblocking;
4827 },4829 },
4828 };4830 };
48294831
...@@ -4832,7 +4834,7 @@ fn dirOpenFilePosix(...@@ -4832,7 +4834,7 @@ fn dirOpenFilePosix(
4832 const fd: posix.fd_t = fd: {4834 const fd: posix.fd_t = fd: {
4833 const syscall: Syscall = try .start();4835 const syscall: Syscall = try .start();
4834 while (true) {4836 while (true) {
4835 const rc = openat_sym(dir.handle, sub_path_posix, os_flags, mode);4837 const rc = openat_sym(dir.handle, sub_path_posix, flags, mode);
4836 switch (posix.errno(rc)) {4838 switch (posix.errno(rc)) {
4837 .SUCCESS => {4839 .SUCCESS => {
4838 syscall.finish();4840 syscall.finish();
...@@ -4878,7 +4880,7 @@ fn dirOpenFilePosix(...@@ -4878,7 +4880,7 @@ fn dirOpenFilePosix(
4878 };4880 };
4879 errdefer closeFd(fd);4881 errdefer closeFd(fd);
48804882
4881 if (!flags.allow_directory) {4883 if (!options.allow_directory) {
4882 const is_dir = is_dir: {4884 const is_dir = is_dir: {
4883 const stat = fileStat(t, .{4885 const stat = fileStat(t, .{
4884 .handle = fd,4886 .handle = fd,
...@@ -4893,9 +4895,9 @@ fn dirOpenFilePosix(...@@ -4893,9 +4895,9 @@ fn dirOpenFilePosix(
4893 if (is_dir) return error.IsDir;4895 if (is_dir) return error.IsDir;
4894 }4896 }
48954897
4896 if (have_flock and !have_flock_open_flags and flags.lock != .none) {4898 if (have_flock and !have_flock_open_flags and options.lock != .none) {
4897 const lock_nonblocking: i32 = if (flags.lock_nonblocking) posix.LOCK.NB else 0;4899 const lock_nonblocking: i32 = if (options.lock_nonblocking) posix.LOCK.NB else 0;
4898 const lock_flags = switch (flags.lock) {4900 const lock_flags = switch (options.lock) {
4899 .none => unreachable,4901 .none => unreachable,
4900 .shared => posix.LOCK.SH | lock_nonblocking,4902 .shared => posix.LOCK.SH | lock_nonblocking,
4901 .exclusive => posix.LOCK.EX | lock_nonblocking,4903 .exclusive => posix.LOCK.EX | lock_nonblocking,
...@@ -4926,7 +4928,7 @@ fn dirOpenFilePosix(...@@ -4926,7 +4928,7 @@ fn dirOpenFilePosix(
4926 }4928 }
4927 }4929 }
49284930
4929 if (have_flock_open_flags and flags.lock_nonblocking) {4931 if (have_flock_open_flags and options.lock_nonblocking) {
4930 var fl_flags: usize = fl: {4932 var fl_flags: usize = fl: {
4931 const syscall: Syscall = try .start();4933 const syscall: Syscall = try .start();
4932 while (true) {4934 while (true) {
...@@ -4979,7 +4981,7 @@ fn dirOpenFileWindows(...@@ -4979,7 +4981,7 @@ fn dirOpenFileWindows(
4979 userdata: ?*anyopaque,4981 userdata: ?*anyopaque,
4980 dir: Dir,4982 dir: Dir,
4981 sub_path: []const u8,4983 sub_path: []const u8,
4982 flags: File.OpenFlags,4984 flags: Dir.OpenFileOptions,
4983) File.OpenError!File {4985) File.OpenError!File {
4984 const t: *Threaded = @ptrCast(@alignCast(userdata));4986 const t: *Threaded = @ptrCast(@alignCast(userdata));
4985 _ = t;4987 _ = t;
...@@ -4992,7 +4994,7 @@ fn dirOpenFileWindows(...@@ -4992,7 +4994,7 @@ fn dirOpenFileWindows(
4992pub fn dirOpenFileWtf16(4994pub fn dirOpenFileWtf16(
4993 dir_handle: ?windows.HANDLE,4995 dir_handle: ?windows.HANDLE,
4994 sub_path_w: []const u16,4996 sub_path_w: []const u16,
4995 flags: File.OpenFlags,4997 flags: Dir.OpenFileOptions,
4996) File.OpenError!File {4998) File.OpenError!File {
4997 const allow_directory = flags.allow_directory and !flags.isWrite();4999 const allow_directory = flags.allow_directory and !flags.isWrite();
4998 if (!allow_directory and std.mem.eql(u16, sub_path_w, &.{'.'})) return error.IsDir;5000 if (!allow_directory and std.mem.eql(u16, sub_path_w, &.{'.'})) return error.IsDir;
...@@ -5129,7 +5131,7 @@ fn dirOpenFileWasi(...@@ -5129,7 +5131,7 @@ fn dirOpenFileWasi(
5129 userdata: ?*anyopaque,5131 userdata: ?*anyopaque,
5130 dir: Dir,5132 dir: Dir,
5131 sub_path: []const u8,5133 sub_path: []const u8,
5132 flags: File.OpenFlags,5134 flags: Dir.OpenFileOptions,
5133) File.OpenError!File {5135) File.OpenError!File {
5134 if (builtin.link_libc) return dirOpenFilePosix(userdata, dir, sub_path, flags);5136 if (builtin.link_libc) return dirOpenFilePosix(userdata, dir, sub_path, flags);
5135 const t: *Threaded = @ptrCast(@alignCast(userdata));5137 const t: *Threaded = @ptrCast(@alignCast(userdata));
...@@ -10193,7 +10195,7 @@ fn posixSeekTo(fd: posix.fd_t, offset: u64) File.SeekError!void {...@@ -10193,7 +10195,7 @@ fn posixSeekTo(fd: posix.fd_t, offset: u64) File.SeekError!void {
10193 }10195 }
10194}10196}
1019510197
10196fn processExecutableOpen(userdata: ?*anyopaque, flags: File.OpenFlags) process.OpenExecutableError!File {10198fn processExecutableOpen(userdata: ?*anyopaque, flags: Dir.OpenFileOptions) process.OpenExecutableError!File {
10197 const t: *Threaded = @ptrCast(@alignCast(userdata));10199 const t: *Threaded = @ptrCast(@alignCast(userdata));
10198 switch (native_os) {10200 switch (native_os) {
10199 .wasi => return error.OperationUnsupported,10201 .wasi => return error.OperationUnsupported,
lib/std/c.zig+2-1
...@@ -8535,7 +8535,8 @@ pub const O = switch (native_os) {...@@ -8535,7 +8535,8 @@ pub const O = switch (native_os) {
8535 CREAT: bool = false,8535 CREAT: bool = false,
8536 TRUNC: bool = false,8536 TRUNC: bool = false,
8537 EXCL: bool = false,8537 EXCL: bool = false,
8538 _12: u3 = 0,8538 RESOLVE_BENEATH: bool = false,
8539 _13: u2 = 0,
8539 EVTONLY: bool = false,8540 EVTONLY: bool = false,
8540 _16: u1 = 0,8541 _16: u1 = 0,
8541 NOCTTY: bool = false,8542 NOCTTY: bool = false,