authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-03-21 00:04:10+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-03-21 00:04:10+01:00
loge938344100da959308aa27dee1295e5ce02efc30
treee549e778aef7a4a0fa4d20dc83ca94a59070706a
parent4c3877069dbf3476b15cea9258d164bda26e8385
parenta9e5c72aa885c67348277ca982fb964fd686254e

Merge pull request 'linux: fix handling of O_TMPFILE flag on filesystems that do not support it' (#31543) from eshom/zig:tmpfile-not-supported into master

Reviewed-on: https://codeberg.org/ziglang/zig/pulls/31543 Reviewed-by: Andrew Kelley <andrew@ziglang.org>

2 files changed, 23 insertions(+), 17 deletions(-)

lib/std/Io/Threaded.zig+1-1
...@@ -4704,7 +4704,7 @@ fn dirCreateFileAtomic(...@@ -4704,7 +4704,7 @@ fn dirCreateFileAtomic(
4704 try syscall.checkCancel();4704 try syscall.checkCancel();
4705 continue;4705 continue;
4706 },4706 },
4707 .ISDIR, .NOENT => {4707 .ISDIR, .NOENT, .OPNOTSUPP => {
4708 // Ambiguous error code. It might mean the file system4708 // Ambiguous error code. It might mean the file system
4709 // does not support O_TMPFILE. Therefore, we must fall4709 // does not support O_TMPFILE. Therefore, we must fall
4710 // back to not using O_TMPFILE.4710 // back to not using O_TMPFILE.
lib/std/Io/Uring.zig+22-16
...@@ -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, // TMPFILE unset.
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;
...@@ -2722,12 +2725,10 @@ fn dirOpenDir(...@@ -2722,12 +2725,10 @@ fn dirOpenDir(
2722 error.WouldBlock => return errnoBug(.AGAIN),2725 error.WouldBlock => return errnoBug(.AGAIN),
2723 error.FileTooBig => return errnoBug(.FBIG),2726 error.FileTooBig => return errnoBug(.FBIG),
2724 error.NoSpaceLeft => return errnoBug(.NOSPC),2727 error.NoSpaceLeft => return errnoBug(.NOSPC),
2725 error.DeviceBusy => return errnoBug(.BUSY), // O_EXCL not passed2728 error.DeviceBusy => return errnoBug(.BUSY), // EXCL unset.
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.2731 error.OperationUnsupported => return errnoBug(.OPNOTSUPP), // No TMPFILE, no locks.
2729 error.AntivirusInterference => unreachable, // Windows-only
2730 error.FileLocksUnsupported => return errnoBug(.OPNOTSUPP), // Not asking for locks.
2731 else => |e| return e,2732 else => |e| return e,
2732 },2733 },
2733 };2734 };
...@@ -2810,13 +2811,16 @@ fn dirCreateFile(...@@ -2810,13 +2811,16 @@ fn dirCreateFile(
28102811
2811 var maybe_sync: CancelRegion.Sync.Maybe = .{ .cancel_region = .init() };2812 var maybe_sync: CancelRegion.Sync.Maybe = .{ .cancel_region = .init() };
2812 defer maybe_sync.deinit(ev);2813 defer maybe_sync.deinit(ev);
2813 const fd = try ev.openat(&maybe_sync.cancel_region, dir.handle, sub_path_posix, .{2814 const fd = ev.openat(&maybe_sync.cancel_region, dir.handle, sub_path_posix, .{
2814 .ACCMODE = if (flags.read) .RDWR else .WRONLY,2815 .ACCMODE = if (flags.read) .RDWR else .WRONLY,
2815 .CREAT = true,2816 .CREAT = true,
2816 .TRUNC = flags.truncate,2817 .TRUNC = flags.truncate,
2817 .EXCL = flags.exclusive,2818 .EXCL = flags.exclusive,
2818 .CLOEXEC = true,2819 .CLOEXEC = true,
2819 }, flags.permissions.toMode());2820 }, flags.permissions.toMode()) catch |err| switch (err) {
2821 error.OperationUnsupported => return error.Unexpected, // TMPFILE unset.
2822 else => |e| return e,
2823 };
2820 errdefer ev.closeAsync(fd);2824 errdefer ev.closeAsync(fd);
28212825
2822 switch (flags.lock) {2826 switch (flags.lock) {
...@@ -2892,7 +2896,7 @@ fn dirCreateFileAtomic(...@@ -2892,7 +2896,7 @@ fn dirCreateFileAtomic(
2892 flags,2896 flags,
2893 options.permissions.toMode(),2897 options.permissions.toMode(),
2894 ) catch |err| switch (err) {2898 ) catch |err| switch (err) {
2895 error.IsDir, error.FileNotFound => {2899 error.IsDir, error.FileNotFound, error.OperationUnsupported => {
2896 // Ambiguous error code. It might mean the file system2900 // Ambiguous error code. It might mean the file system
2897 // does not support O_TMPFILE. Therefore, we must fall2901 // does not support O_TMPFILE. Therefore, we must fall
2898 // back to not using O_TMPFILE.2902 // back to not using O_TMPFILE.
...@@ -2901,9 +2905,6 @@ fn dirCreateFileAtomic(...@@ -2901,9 +2905,6 @@ fn dirCreateFileAtomic(
2901 error.FileTooBig => return errnoBug(.FBIG),2905 error.FileTooBig => return errnoBug(.FBIG),
2902 error.DeviceBusy => return errnoBug(.BUSY), // O_EXCL not passed2906 error.DeviceBusy => return errnoBug(.BUSY), // O_EXCL not passed
2903 error.PathAlreadyExists => return errnoBug(.EXIST), // Not creating.2907 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.
2907 else => |e| return e,2908 else => |e| return e,
2908 },2909 },
2909 .flags = .{ .nonblocking = false },2910 .flags = .{ .nonblocking = false },
...@@ -2994,7 +2995,7 @@ fn dirOpenFile(...@@ -2994,7 +2995,7 @@ fn dirOpenFile(
29942995
2995 var maybe_sync: CancelRegion.Sync.Maybe = .{ .cancel_region = .init() };2996 var maybe_sync: CancelRegion.Sync.Maybe = .{ .cancel_region = .init() };
2996 defer maybe_sync.deinit(ev);2997 defer maybe_sync.deinit(ev);
2997 const fd = try ev.openat(&maybe_sync.cancel_region, dir.handle, sub_path_posix, .{2998 const fd = ev.openat(&maybe_sync.cancel_region, dir.handle, sub_path_posix, .{
2998 .ACCMODE = switch (flags.mode) {2999 .ACCMODE = switch (flags.mode) {
2999 .read_only => .RDONLY,3000 .read_only => .RDONLY,
3000 .write_only => .WRONLY,3001 .write_only => .WRONLY,
...@@ -3004,7 +3005,10 @@ fn dirOpenFile(...@@ -3004,7 +3005,10 @@ fn dirOpenFile(
3004 .NOFOLLOW = !flags.follow_symlinks,3005 .NOFOLLOW = !flags.follow_symlinks,
3005 .CLOEXEC = true,3006 .CLOEXEC = true,
3006 .PATH = flags.path_only,3007 .PATH = flags.path_only,
3007 }, 0);3008 }, 0) catch |err| switch (err) {
3009 error.OperationUnsupported => return error.Unexpected, // TMPFILE unset.
3010 else => |e| return e,
3011 };
3008 errdefer ev.closeAsync(fd);3012 errdefer ev.closeAsync(fd);
30093013
3010 if (!flags.allow_directory) {3014 if (!flags.allow_directory) {
...@@ -3149,7 +3153,7 @@ fn dirRealPathFile(...@@ -3149,7 +3153,7 @@ fn dirRealPathFile(
3149 .PATH = true,3153 .PATH = true,
3150 }, 0) catch |err| switch (err) {3154 }, 0) catch |err| switch (err) {
3151 error.WouldBlock => return errnoBug(.AGAIN),3155 error.WouldBlock => return errnoBug(.AGAIN),
3152 error.FileLocksUnsupported => return errnoBug(.OPNOTSUPP), // Not asking for locks.3156 error.OperationUnsupported => return errnoBug(.OPNOTSUPP), // Not asking for locks.
3153 else => |e| return e,3157 else => |e| return e,
3154 };3158 };
3155 defer ev.closeAsync(fd);3159 defer ev.closeAsync(fd);
...@@ -5616,7 +5620,7 @@ fn openat(...@@ -5616,7 +5620,7 @@ fn openat(
5616 path: [*:0]const u8,5620 path: [*:0]const u8,
5617 flags: linux.O,5621 flags: linux.O,
5618 mode: linux.mode_t,5622 mode: linux.mode_t,
5619) File.OpenError!fd_t {5623) !fd_t {
5620 var mut_flags = flags;5624 var mut_flags = flags;
5621 if (@hasField(linux.O, "LARGEFILE")) mut_flags.LARGEFILE = true;5625 if (@hasField(linux.O, "LARGEFILE")) mut_flags.LARGEFILE = true;
5622 while (true) {5626 while (true) {
...@@ -5662,7 +5666,9 @@ fn openat(...@@ -5662,7 +5666,9 @@ fn openat(
5662 .PERM => return error.PermissionDenied,5666 .PERM => return error.PermissionDenied,
5663 .EXIST => return error.PathAlreadyExists,5667 .EXIST => return error.PathAlreadyExists,
5664 .BUSY => return error.DeviceBusy,5668 .BUSY => return error.DeviceBusy,
5665 .OPNOTSUPP => return error.FileLocksUnsupported,5669 // This can be triggered by file locking and TMPFILE, but those
5670 // flags are mutually exclusive.
5671 .OPNOTSUPP => return error.OperationUnsupported,
5666 .AGAIN => return error.WouldBlock,5672 .AGAIN => return error.WouldBlock,
5667 .TXTBSY => return error.FileBusy,5673 .TXTBSY => return error.FileBusy,
5668 .NXIO => return error.NoDevice,5674 .NXIO => return error.NoDevice,