authorgravatar for eshom@noreply.codeberg.orgeshom <eshom@noreply.codeberg.org> 2026-03-18 22:38:22+02:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-03-20 12:12:29-07:00
log2054a257c288c44df52510ac2e7646a08df8a4bb
tree665d1f2e97eb8fb1af9b165f4d987fd8f20e3eaa
parent5119cf6ffd0cf92586bc26f3f2d6e1e623fd17a8

std.Io.Uring: handle UnsupportedOperation for O_TMPFILE case

Having `std.Io.Uring` contain OpenError error set allows handling of OperationUnsupported specifically for `.openat`, while avoiding propagating this error to the more general `File.OpenError`. This more specific subset also eliminated 2 unreachable prongs that previously inherited from `File.OpenError`. Added comments when handling OperationUnsupported, making it clear it is an unexpected error when TMPFILE bit is not set.

1 files changed, 40 insertions(+), 12 deletions(-)

lib/std/Io/Uring.zig+40-12
...@@ -593,7 +593,10 @@ const CachedFd = struct {...@@ -593,7 +593,10 @@ const CachedFd = struct {
593 @atomicStore(Once, &cached_fd.once, .uninitialized, .monotonic);593 @atomicStore(Once, &cached_fd.once, .uninitialized, .monotonic);
594 futexWake(ev, @ptrCast(&cached_fd.once), 1);594 futexWake(ev, @ptrCast(&cached_fd.once), 1);
595 }595 }
596 const fd = try ev.openat(cancel_region, linux.AT.FDCWD, path, flags, 0);596 const fd = ev.openat(cancel_region, linux.AT.FDCWD, path, flags, 0) catch |err| switch (err) {
597 error.OperationUnsupported => return error.Unexpected, // Not expecting O_TMPFILE flag
598 else => |e| return e,
599 };
597 @atomicStore(Once, &cached_fd.once, .fromFd(fd), .monotonic);600 @atomicStore(Once, &cached_fd.once, .fromFd(fd), .monotonic);
598 futexWake(ev, @ptrCast(&cached_fd.once), std.math.maxInt(u32));601 futexWake(ev, @ptrCast(&cached_fd.once), std.math.maxInt(u32));
599 return fd;602 return fd;
...@@ -2725,9 +2728,8 @@ fn dirOpenDir(...@@ -2725,9 +2728,8 @@ fn dirOpenDir(
2725 error.DeviceBusy => return errnoBug(.BUSY), // O_EXCL not passed2728 error.DeviceBusy => return errnoBug(.BUSY), // O_EXCL not passed
2726 error.FileBusy => return errnoBug(.TXTBSY),2729 error.FileBusy => return errnoBug(.TXTBSY),
2727 error.PathAlreadyExists => return errnoBug(.EXIST), // Not creating.2730 error.PathAlreadyExists => return errnoBug(.EXIST), // Not creating.
2728 error.PipeBusy => return error.Unexpected, // Not opening a pipe.
2729 error.AntivirusInterference => unreachable, // Windows-only
2730 error.FileLocksUnsupported => return errnoBug(.OPNOTSUPP), // Not asking for locks.2731 error.FileLocksUnsupported => return errnoBug(.OPNOTSUPP), // Not asking for locks.
2732 error.OperationUnsupported => return errnoBug(.OPNOTSUPP), // Not asking for O_TMPFILE.
2731 else => |e| return e,2733 else => |e| return e,
2732 },2734 },
2733 };2735 };
...@@ -2810,13 +2812,16 @@ fn dirCreateFile(...@@ -2810,13 +2812,16 @@ fn dirCreateFile(
28102812
2811 var maybe_sync: CancelRegion.Sync.Maybe = .{ .cancel_region = .init() };2813 var maybe_sync: CancelRegion.Sync.Maybe = .{ .cancel_region = .init() };
2812 defer maybe_sync.deinit(ev);2814 defer maybe_sync.deinit(ev);
2813 const fd = try ev.openat(&maybe_sync.cancel_region, dir.handle, sub_path_posix, .{2815 const fd = ev.openat(&maybe_sync.cancel_region, dir.handle, sub_path_posix, .{
2814 .ACCMODE = if (flags.read) .RDWR else .WRONLY,2816 .ACCMODE = if (flags.read) .RDWR else .WRONLY,
2815 .CREAT = true,2817 .CREAT = true,
2816 .TRUNC = flags.truncate,2818 .TRUNC = flags.truncate,
2817 .EXCL = flags.exclusive,2819 .EXCL = flags.exclusive,
2818 .CLOEXEC = true,2820 .CLOEXEC = true,
2819 }, flags.permissions.toMode());2821 }, flags.permissions.toMode()) catch |err| switch (err) {
2822 error.OperationUnsupported => return error.Unexpected, // TMPFILE bit not set.
2823 else => |e| return e,
2824 };
2820 errdefer ev.closeAsync(fd);2825 errdefer ev.closeAsync(fd);
28212826
2822 switch (flags.lock) {2827 switch (flags.lock) {
...@@ -2892,7 +2897,7 @@ fn dirCreateFileAtomic(...@@ -2892,7 +2897,7 @@ fn dirCreateFileAtomic(
2892 flags,2897 flags,
2893 options.permissions.toMode(),2898 options.permissions.toMode(),
2894 ) catch |err| switch (err) {2899 ) catch |err| switch (err) {
2895 error.IsDir, error.FileNotFound => {2900 error.IsDir, error.FileNotFound, error.OperationUnsupported => {
2896 // Ambiguous error code. It might mean the file system2901 // Ambiguous error code. It might mean the file system
2897 // does not support O_TMPFILE. Therefore, we must fall2902 // does not support O_TMPFILE. Therefore, we must fall
2898 // back to not using O_TMPFILE.2903 // back to not using O_TMPFILE.
...@@ -2901,8 +2906,6 @@ fn dirCreateFileAtomic(...@@ -2901,8 +2906,6 @@ fn dirCreateFileAtomic(
2901 error.FileTooBig => return errnoBug(.FBIG),2906 error.FileTooBig => return errnoBug(.FBIG),
2902 error.DeviceBusy => return errnoBug(.BUSY), // O_EXCL not passed2907 error.DeviceBusy => return errnoBug(.BUSY), // O_EXCL not passed
2903 error.PathAlreadyExists => return errnoBug(.EXIST), // Not creating.2908 error.PathAlreadyExists => return errnoBug(.EXIST), // Not creating.
2904 error.PipeBusy => return error.Unexpected, // Not opening a pipe.
2905 error.AntivirusInterference => unreachable, // Windows-only
2906 error.FileLocksUnsupported => return errnoBug(.OPNOTSUPP), // Not asking for locks.2909 error.FileLocksUnsupported => return errnoBug(.OPNOTSUPP), // Not asking for locks.
2907 else => |e| return e,2910 else => |e| return e,
2908 },2911 },
...@@ -2994,7 +2997,7 @@ fn dirOpenFile(...@@ -2994,7 +2997,7 @@ fn dirOpenFile(
29942997
2995 var maybe_sync: CancelRegion.Sync.Maybe = .{ .cancel_region = .init() };2998 var maybe_sync: CancelRegion.Sync.Maybe = .{ .cancel_region = .init() };
2996 defer maybe_sync.deinit(ev);2999 defer maybe_sync.deinit(ev);
2997 const fd = try ev.openat(&maybe_sync.cancel_region, dir.handle, sub_path_posix, .{3000 const fd = ev.openat(&maybe_sync.cancel_region, dir.handle, sub_path_posix, .{
2998 .ACCMODE = switch (flags.mode) {3001 .ACCMODE = switch (flags.mode) {
2999 .read_only => .RDONLY,3002 .read_only => .RDONLY,
3000 .write_only => .WRONLY,3003 .write_only => .WRONLY,
...@@ -3004,7 +3007,10 @@ fn dirOpenFile(...@@ -3004,7 +3007,10 @@ fn dirOpenFile(
3004 .NOFOLLOW = !flags.follow_symlinks,3007 .NOFOLLOW = !flags.follow_symlinks,
3005 .CLOEXEC = true,3008 .CLOEXEC = true,
3006 .PATH = flags.path_only,3009 .PATH = flags.path_only,
3007 }, 0);3010 }, 0) catch |err| switch (err) {
3011 error.OperationUnsupported => return error.Unexpected, // TMPFILE bit not set.
3012 else => |e| return e,
3013 };
3008 errdefer ev.closeAsync(fd);3014 errdefer ev.closeAsync(fd);
30093015
3010 if (!flags.allow_directory) {3016 if (!flags.allow_directory) {
...@@ -5609,6 +5615,27 @@ fn lseek(...@@ -5609,6 +5615,27 @@ fn lseek(
5609 }5615 }
5610}5616}
56115617
5618const OpenError = error{
5619 AccessDenied,
5620 FileTooBig,
5621 IsDir,
5622 SymLinkLoop,
5623 ProcessFdQuotaExceeded,
5624 SystemFdQuotaExceeded,
5625 NoDevice,
5626 FileNotFound,
5627 SystemResources,
5628 NoSpaceLeft,
5629 NotDir,
5630 PermissionDenied,
5631 PathAlreadyExists,
5632 DeviceBusy,
5633 OperationUnsupported,
5634 FileLocksUnsupported,
5635 WouldBlock,
5636 FileBusy,
5637} || Dir.PathNameError || Io.Cancelable || Io.UnexpectedError;
5638
5612fn openat(5639fn openat(
5613 ev: *Evented,5640 ev: *Evented,
5614 cancel_region: *CancelRegion,5641 cancel_region: *CancelRegion,
...@@ -5616,7 +5643,7 @@ fn openat(...@@ -5616,7 +5643,7 @@ fn openat(
5616 path: [*:0]const u8,5643 path: [*:0]const u8,
5617 flags: linux.O,5644 flags: linux.O,
5618 mode: linux.mode_t,5645 mode: linux.mode_t,
5619) File.OpenError!fd_t {5646) OpenError!fd_t {
5620 var mut_flags = flags;5647 var mut_flags = flags;
5621 if (@hasField(linux.O, "LARGEFILE")) mut_flags.LARGEFILE = true;5648 if (@hasField(linux.O, "LARGEFILE")) mut_flags.LARGEFILE = true;
5622 while (true) {5649 while (true) {
...@@ -5662,7 +5689,8 @@ fn openat(...@@ -5662,7 +5689,8 @@ fn openat(
5662 .PERM => return error.PermissionDenied,5689 .PERM => return error.PermissionDenied,
5663 .EXIST => return error.PathAlreadyExists,5690 .EXIST => return error.PathAlreadyExists,
5664 .BUSY => return error.DeviceBusy,5691 .BUSY => return error.DeviceBusy,
5665 .OPNOTSUPP => return error.FileLocksUnsupported,5692 // File locking and TMPFILE are mutually exclusive
5693 .OPNOTSUPP => return if (flags.TMPFILE) error.OperationUnsupported else error.FileLocksUnsupported,
5666 .AGAIN => return error.WouldBlock,5694 .AGAIN => return error.WouldBlock,
5667 .TXTBSY => return error.FileBusy,5695 .TXTBSY => return error.FileBusy,
5668 .NXIO => return error.NoDevice,5696 .NXIO => return error.NoDevice,